aboutsummaryrefslogtreecommitdiff
path: root/middleware
diff options
context:
space:
mode:
Diffstat (limited to 'middleware')
-rw-r--r--middleware/auto/README.md68
-rw-r--r--middleware/auto/auto.go96
-rw-r--r--middleware/auto/regexp.go20
-rw-r--r--middleware/auto/regexp_test.go20
-rw-r--r--middleware/auto/setup.go172
-rw-r--r--middleware/auto/setup_test.go125
-rw-r--r--middleware/auto/walk.go109
-rw-r--r--middleware/auto/walk_test.go94
-rw-r--r--middleware/auto/watcher_test.go58
-rw-r--r--middleware/auto/zone.go76
-rw-r--r--middleware/autopath/README.md45
-rw-r--r--middleware/autopath/autopath.go152
-rw-r--r--middleware/autopath/autopath_test.go166
-rw-r--r--middleware/autopath/cname.go25
-rw-r--r--middleware/autopath/setup.go93
-rw-r--r--middleware/autopath/setup_test.go77
-rw-r--r--middleware/backend.go32
-rw-r--r--middleware/backend_lookup.go410
-rw-r--r--middleware/bind/README.md22
-rw-r--r--middleware/bind/bind.go11
-rw-r--r--middleware/bind/bind_test.go30
-rw-r--r--middleware/bind/setup.go24
-rw-r--r--middleware/cache/README.md68
-rw-r--r--middleware/cache/cache.go167
-rw-r--r--middleware/cache/cache_test.go251
-rw-r--r--middleware/cache/freq/freq.go55
-rw-r--r--middleware/cache/freq/freq_test.go36
-rw-r--r--middleware/cache/handler.go119
-rw-r--r--middleware/cache/item.go116
-rw-r--r--middleware/cache/prefech_test.go54
-rw-r--r--middleware/cache/setup.go170
-rw-r--r--middleware/cache/setup_test.go94
-rw-r--r--middleware/chaos/README.md46
-rw-r--r--middleware/chaos/chaos.go62
-rw-r--r--middleware/chaos/chaos_test.go80
-rw-r--r--middleware/chaos/setup.go55
-rw-r--r--middleware/chaos/setup_test.go54
-rw-r--r--middleware/debug/README.md20
-rw-r--r--middleware/debug/debug.go28
-rw-r--r--middleware/debug/debug_test.go49
-rw-r--r--middleware/dnssec/README.md88
-rw-r--r--middleware/dnssec/black_lies.go24
-rw-r--r--middleware/dnssec/black_lies_test.go49
-rw-r--r--middleware/dnssec/cache.go22
-rw-r--r--middleware/dnssec/cache_test.go34
-rw-r--r--middleware/dnssec/dnskey.go72
-rw-r--r--middleware/dnssec/dnssec.go135
-rw-r--r--middleware/dnssec/dnssec_test.go219
-rw-r--r--middleware/dnssec/handler.go82
-rw-r--r--middleware/dnssec/handler_test.go155
-rw-r--r--middleware/dnssec/responsewriter.go49
-rw-r--r--middleware/dnssec/rrsig.go53
-rw-r--r--middleware/dnssec/setup.go128
-rw-r--r--middleware/dnssec/setup_test.go120
-rw-r--r--middleware/dnstap/README.md61
-rw-r--r--middleware/dnstap/handler.go79
-rw-r--r--middleware/dnstap/handler_test.go65
-rw-r--r--middleware/dnstap/msg/msg.go168
-rw-r--r--middleware/dnstap/msg/msg_test.go42
-rw-r--r--middleware/dnstap/msg/wrapper.go26
-rw-r--r--middleware/dnstap/out/socket.go86
-rw-r--r--middleware/dnstap/out/socket_test.go94
-rw-r--r--middleware/dnstap/out/tcp.go59
-rw-r--r--middleware/dnstap/out/tcp_test.go66
-rw-r--r--middleware/dnstap/setup.go98
-rw-r--r--middleware/dnstap/setup_test.go34
-rw-r--r--middleware/dnstap/taprw/writer.go73
-rw-r--r--middleware/dnstap/taprw/writer_test.go82
-rw-r--r--middleware/dnstap/test/helpers.go80
-rw-r--r--middleware/erratic/README.md76
-rw-r--r--middleware/erratic/autopath.go8
-rw-r--r--middleware/erratic/erratic.go95
-rw-r--r--middleware/erratic/erratic_test.go79
-rw-r--r--middleware/erratic/setup.go117
-rw-r--r--middleware/erratic/setup_test.go103
-rw-r--r--middleware/errors/README.md22
-rw-r--r--middleware/errors/errors.go79
-rw-r--r--middleware/errors/errors_test.go73
-rw-r--r--middleware/errors/setup.go55
-rw-r--r--middleware/errors/setup_test.go45
-rw-r--r--middleware/etcd/README.md109
-rw-r--r--middleware/etcd/cname_test.go79
-rw-r--r--middleware/etcd/etcd.go188
-rw-r--r--middleware/etcd/group_test.go74
-rw-r--r--middleware/etcd/handler.go97
-rw-r--r--middleware/etcd/lookup_test.go273
-rw-r--r--middleware/etcd/msg/path.go48
-rw-r--r--middleware/etcd/msg/path_test.go12
-rw-r--r--middleware/etcd/msg/service.go203
-rw-r--r--middleware/etcd/msg/service_test.go125
-rw-r--r--middleware/etcd/msg/type.go33
-rw-r--r--middleware/etcd/msg/type_test.go31
-rw-r--r--middleware/etcd/multi_test.go59
-rw-r--r--middleware/etcd/other_test.go150
-rw-r--r--middleware/etcd/setup.go144
-rw-r--r--middleware/etcd/setup_test.go64
-rw-r--r--middleware/etcd/stub.go82
-rw-r--r--middleware/etcd/stub_handler.go86
-rw-r--r--middleware/etcd/stub_test.go88
-rw-r--r--middleware/federation/README.md43
-rw-r--r--middleware/federation/federation.go141
-rw-r--r--middleware/federation/federation_test.go81
-rw-r--r--middleware/federation/kubernetes_api_test.go111
-rw-r--r--middleware/federation/setup.go89
-rw-r--r--middleware/federation/setup_test.go65
-rw-r--r--middleware/file/README.md55
-rw-r--r--middleware/file/closest.go24
-rw-r--r--middleware/file/closest_test.go38
-rw-r--r--middleware/file/cname_test.go124
-rw-r--r--middleware/file/delegation_test.go207
-rw-r--r--middleware/file/dname.go44
-rw-r--r--middleware/file/dname_test.go300
-rw-r--r--middleware/file/dnssec_test.go358
-rw-r--r--middleware/file/dnssex_test.go145
-rw-r--r--middleware/file/ds_test.go75
-rw-r--r--middleware/file/ent_test.go159
-rw-r--r--middleware/file/example_org.go113
-rw-r--r--middleware/file/file.go138
-rw-r--r--middleware/file/file_test.go31
-rw-r--r--middleware/file/glue_test.go253
-rw-r--r--middleware/file/include_test.go32
-rw-r--r--middleware/file/lookup.go467
-rw-r--r--middleware/file/lookup_test.go194
-rw-r--r--middleware/file/notify.go82
-rw-r--r--middleware/file/nsec3_test.go28
-rw-r--r--middleware/file/reload.go72
-rw-r--r--middleware/file/reload_test.go82
-rw-r--r--middleware/file/secondary.go199
-rw-r--r--middleware/file/secondary_test.go168
-rw-r--r--middleware/file/setup.go171
-rw-r--r--middleware/file/setup_test.go77
-rw-r--r--middleware/file/tree/all.go48
-rw-r--r--middleware/file/tree/elem.go136
-rw-r--r--middleware/file/tree/less.go59
-rw-r--r--middleware/file/tree/less_test.go81
-rw-r--r--middleware/file/tree/print.go62
-rw-r--r--middleware/file/tree/tree.go455
-rw-r--r--middleware/file/wildcard.go13
-rw-r--r--middleware/file/wildcard_test.go289
-rw-r--r--middleware/file/xfr.go62
-rw-r--r--middleware/file/xfr_test.go34
-rw-r--r--middleware/file/zone.go190
-rw-r--r--middleware/file/zone_test.go30
-rw-r--r--middleware/health/README.md23
-rw-r--r--middleware/health/health.go69
-rw-r--r--middleware/health/health_test.go47
-rw-r--r--middleware/health/healther.go42
-rw-r--r--middleware/health/setup.go73
-rw-r--r--middleware/health/setup_test.go35
-rw-r--r--middleware/hosts/README.md45
-rw-r--r--middleware/hosts/hosts.go136
-rw-r--r--middleware/hosts/hosts_test.go75
-rw-r--r--middleware/hosts/hostsfile.go193
-rw-r--r--middleware/hosts/hostsfile_test.go239
-rw-r--r--middleware/hosts/setup.go88
-rw-r--r--middleware/hosts/setup_test.go86
-rw-r--r--middleware/kubernetes/DEV-README.md43
-rw-r--r--middleware/kubernetes/README.md167
-rw-r--r--middleware/kubernetes/apiproxy.go76
-rw-r--r--middleware/kubernetes/autopath.go53
-rw-r--r--middleware/kubernetes/controller.go399
-rw-r--r--middleware/kubernetes/federation.go45
-rw-r--r--middleware/kubernetes/handler.go86
-rw-r--r--middleware/kubernetes/handler_pod_disabled_test.go61
-rw-r--r--middleware/kubernetes/handler_pod_insecure_test.go59
-rw-r--r--middleware/kubernetes/handler_pod_verified_test.go59
-rw-r--r--middleware/kubernetes/handler_test.go347
-rw-r--r--middleware/kubernetes/kubernetes.go457
-rw-r--r--middleware/kubernetes/kubernetes_apex_test.go68
-rw-r--r--middleware/kubernetes/kubernetes_test.go242
-rw-r--r--middleware/kubernetes/local.go40
-rw-r--r--middleware/kubernetes/ns.go65
-rw-r--r--middleware/kubernetes/ns_test.go69
-rw-r--r--middleware/kubernetes/parse.go112
-rw-r--r--middleware/kubernetes/parse_test.go56
-rw-r--r--middleware/kubernetes/reverse.go55
-rw-r--r--middleware/kubernetes/reverse_test.go125
-rw-r--r--middleware/kubernetes/setup.go208
-rw-r--r--middleware/kubernetes/setup_reverse_test.go35
-rw-r--r--middleware/kubernetes/setup_test.go473
-rw-r--r--middleware/kubernetes/setup_ttl_test.go45
-rw-r--r--middleware/loadbalance/README.md22
-rw-r--r--middleware/loadbalance/handler.go23
-rw-r--r--middleware/loadbalance/loadbalance.go87
-rw-r--r--middleware/loadbalance/loadbalance_test.go168
-rw-r--r--middleware/loadbalance/setup.go26
-rw-r--r--middleware/log/README.md102
-rw-r--r--middleware/log/log.go91
-rw-r--r--middleware/log/log_test.go101
-rw-r--r--middleware/log/setup.go116
-rw-r--r--middleware/log/setup_test.go130
-rw-r--r--middleware/metrics/README.md53
-rw-r--r--middleware/metrics/handler.go34
-rw-r--r--middleware/metrics/metrics.go101
-rw-r--r--middleware/metrics/metrics_test.go83
-rw-r--r--middleware/metrics/setup.go100
-rw-r--r--middleware/metrics/setup_test.go42
-rw-r--r--middleware/metrics/test/scrape.go225
-rw-r--r--middleware/metrics/vars/report.go62
-rw-r--r--middleware/metrics/vars/vars.go69
-rw-r--r--middleware/middleware.go102
-rw-r--r--middleware/middleware_test.go1
-rw-r--r--middleware/normalize.go137
-rw-r--r--middleware/normalize_test.go84
-rw-r--r--middleware/pkg/cache/cache.go129
-rw-r--r--middleware/pkg/cache/cache_test.go31
-rw-r--r--middleware/pkg/cache/shard_test.go60
-rw-r--r--middleware/pkg/dnsrecorder/recorder.go58
-rw-r--r--middleware/pkg/dnsrecorder/recorder_test.go28
-rw-r--r--middleware/pkg/dnsutil/cname.go15
-rw-r--r--middleware/pkg/dnsutil/cname_test.go55
-rw-r--r--middleware/pkg/dnsutil/dedup.go12
-rw-r--r--middleware/pkg/dnsutil/doc.go2
-rw-r--r--middleware/pkg/dnsutil/host.go82
-rw-r--r--middleware/pkg/dnsutil/host_test.go85
-rw-r--r--middleware/pkg/dnsutil/join.go19
-rw-r--r--middleware/pkg/dnsutil/join_test.go20
-rw-r--r--middleware/pkg/dnsutil/reverse.go68
-rw-r--r--middleware/pkg/dnsutil/reverse_test.go51
-rw-r--r--middleware/pkg/dnsutil/zone.go20
-rw-r--r--middleware/pkg/dnsutil/zone_test.go39
-rw-r--r--middleware/pkg/edns/edns.go46
-rw-r--r--middleware/pkg/edns/edns_test.go37
-rw-r--r--middleware/pkg/healthcheck/healthcheck.go243
-rw-r--r--middleware/pkg/healthcheck/policy.go120
-rw-r--r--middleware/pkg/healthcheck/policy_test.go143
-rw-r--r--middleware/pkg/nonwriter/nonwriter.go23
-rw-r--r--middleware/pkg/nonwriter/nonwriter_test.go19
-rw-r--r--middleware/pkg/rcode/rcode.go16
-rw-r--r--middleware/pkg/rcode/rcode_test.go29
-rw-r--r--middleware/pkg/replacer/replacer.go161
-rw-r--r--middleware/pkg/replacer/replacer_test.go61
-rw-r--r--middleware/pkg/response/classify.go61
-rw-r--r--middleware/pkg/response/typify.go146
-rw-r--r--middleware/pkg/response/typify_test.go84
-rw-r--r--middleware/pkg/singleflight/singleflight.go64
-rw-r--r--middleware/pkg/singleflight/singleflight_test.go85
-rw-r--r--middleware/pkg/tls/tls.go128
-rw-r--r--middleware/pkg/tls/tls_test.go101
-rw-r--r--middleware/pkg/trace/trace.go12
-rw-r--r--middleware/pprof/README.md41
-rw-r--r--middleware/pprof/pprof.go49
-rw-r--r--middleware/pprof/setup.go53
-rw-r--r--middleware/pprof/setup_test.go34
-rw-r--r--middleware/proxy/README.md175
-rw-r--r--middleware/proxy/dns.go106
-rw-r--r--middleware/proxy/dnstap_test.go57
-rw-r--r--middleware/proxy/exchanger.go22
-rw-r--r--middleware/proxy/google.go244
-rw-r--r--middleware/proxy/google_rr.go89
-rw-r--r--middleware/proxy/google_test.go5
-rw-r--r--middleware/proxy/grpc.go96
-rw-r--r--middleware/proxy/grpc_test.go71
-rw-r--r--middleware/proxy/lookup.go132
-rw-r--r--middleware/proxy/metrics.go30
-rw-r--r--middleware/proxy/proxy.go195
-rw-r--r--middleware/proxy/proxy_test.go87
-rw-r--r--middleware/proxy/response.go21
-rw-r--r--middleware/proxy/setup.go46
-rw-r--r--middleware/proxy/upstream.go234
-rw-r--r--middleware/proxy/upstream_test.go324
-rw-r--r--middleware/reverse/README.md86
-rw-r--r--middleware/reverse/network.go87
-rw-r--r--middleware/reverse/network_test.go135
-rw-r--r--middleware/reverse/reverse.go107
-rw-r--r--middleware/reverse/reverse_test.go71
-rw-r--r--middleware/reverse/setup.go147
-rw-r--r--middleware/reverse/setup_test.go195
-rw-r--r--middleware/rewrite/README.md91
-rw-r--r--middleware/rewrite/class.go35
-rw-r--r--middleware/rewrite/condition.go132
-rw-r--r--middleware/rewrite/condition_test.go102
-rw-r--r--middleware/rewrite/edns0.go425
-rw-r--r--middleware/rewrite/name.go24
-rw-r--r--middleware/rewrite/reverter.go39
-rw-r--r--middleware/rewrite/rewrite.go86
-rw-r--r--middleware/rewrite/rewrite_test.go532
-rw-r--r--middleware/rewrite/setup.go42
-rw-r--r--middleware/rewrite/setup_test.go25
-rw-r--r--middleware/rewrite/testdata/testdir/empty0
-rw-r--r--middleware/rewrite/testdata/testfile1
-rw-r--r--middleware/rewrite/type.go37
-rw-r--r--middleware/root/README.md22
-rw-r--r--middleware/root/root.go43
-rw-r--r--middleware/root/root_test.go107
-rw-r--r--middleware/secondary/README.md54
-rw-r--r--middleware/secondary/secondary.go10
-rw-r--r--middleware/secondary/setup.go108
-rw-r--r--middleware/secondary/setup_test.go65
-rw-r--r--middleware/test/doc.go2
-rw-r--r--middleware/test/file.go107
-rw-r--r--middleware/test/file_test.go11
-rw-r--r--middleware/test/helpers.go348
-rw-r--r--middleware/test/responsewriter.go61
-rw-r--r--middleware/test/server.go52
-rw-r--r--middleware/tls/README.md52
-rw-r--r--middleware/tls/tls.go37
-rw-r--r--middleware/tls/tls_test.go44
-rw-r--r--middleware/trace/README.md73
-rw-r--r--middleware/trace/setup.go113
-rw-r--r--middleware/trace/setup_test.go60
-rw-r--r--middleware/trace/trace.go84
-rw-r--r--middleware/trace/trace_test.go33
-rw-r--r--middleware/whoami/README.md44
-rw-r--r--middleware/whoami/setup.go28
-rw-r--r--middleware/whoami/setup_test.go19
-rw-r--r--middleware/whoami/whoami.go57
-rw-r--r--middleware/whoami/whoami_test.go56
308 files changed, 0 insertions, 29222 deletions
diff --git a/middleware/auto/README.md b/middleware/auto/README.md
deleted file mode 100644
index b69c1c291..000000000
--- a/middleware/auto/README.md
+++ /dev/null
@@ -1,68 +0,0 @@
-# auto
-
-*auto* enables serving zone data from an RFC 1035-style master file which is automatically picked
-up from disk.
-
-The *auto* middleware is used for an "old-style" DNS server. It serves from a preloaded file that exists
-on disk. If the zone file contains signatures (i.e. is signed, i.e. DNSSEC) correct DNSSEC answers
-are returned. Only NSEC is supported! If you use this setup *you* are responsible for resigning the
-zonefile. New zones or changed zone are automatically picked up from disk.
-
-## Syntax
-
-~~~
-auto [ZONES...] {
- directory DIR [REGEXP ORIGIN_TEMPLATE [TIMEOUT]]
- no_reload
- upstream ADDRESS...
-}
-~~~
-
-**ZONES** zones it should be authoritative for. If empty, the zones from the configuration block
-are used.
-
-* `directory` loads zones from the speficied **DIR**. If a file name matches **REGEXP** it will be
- used to extract the origin. **ORIGIN_TEMPLATE** will be used as a template for the origin. Strings
- like `{<number>}` are replaced with the respective matches in the file name, i.e. `{1}` is the
- first match, `{2}` is the second, etc.. The default is: `db\.(.*) {1}` e.g. from a file with the
- name `db.example.com`, the extracted origin will be `example.com`. **TIMEOUT** specifies how often
- CoreDNS should scan the directory, the default is every 60 seconds. This value is in seconds.
- The minimum value is 1 second.
-* `no_reload` by default CoreDNS will reload a zone from disk whenever it detects a change to the
- file. This option disables that behavior.
-* `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs)
- pointing to external names. **ADDRESS** can be an IP address, and IP:port or a string pointing to
- a file that is structured as /etc/resolv.conf.
-
-All directives from the *file* middleware are supported. Note that *auto* will load all zones found,
-even though the directive might only receive queries for a specific zone. I.e:
-
-~~~
-auto example.org {
- directory /etc/coredns/zones
-}
-~~~
-Will happily pick up a zone for `example.COM`, except it will never be queried, because the *auto*
-directive only is authoritative for `example.ORG`.
-
-## Examples
-
-Load `org` domains from `/etc/coredns/zones/org` and allow transfers to the internet, but send
-notifies to 10.240.1.1
-
-~~~
-auto org {
- directory /etc/coredns/zones/org
- transfer to *
- transfer to 10.240.1.1
-}
-~~~
-
-Load `org` domains from `/etc/coredns/zones/org` and looks for file names as `www.db.example.org`,
-where `example.org` is the origin. Scan every 45 seconds.
-
-~~~
-auto org {
- directory /etc/coredns/zones/org www\.db\.(.*) {1} 45
-}
-~~~
diff --git a/middleware/auto/auto.go b/middleware/auto/auto.go
deleted file mode 100644
index 31fe53e23..000000000
--- a/middleware/auto/auto.go
+++ /dev/null
@@ -1,96 +0,0 @@
-// Package auto implements an on-the-fly loading file backend.
-package auto
-
-import (
- "regexp"
- "time"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/file"
- "github.com/coredns/coredns/middleware/metrics"
- "github.com/coredns/coredns/middleware/proxy"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-type (
- // Auto holds the zones and the loader configuration for automatically loading zones.
- Auto struct {
- Next middleware.Handler
- *Zones
-
- metrics *metrics.Metrics
- loader
- }
-
- loader struct {
- directory string
- template string
- re *regexp.Regexp
-
- // In the future this should be something like ZoneMeta that contains all this stuff.
- transferTo []string
- noReload bool
- proxy proxy.Proxy // Proxy for looking up names during the resolution process
-
- duration time.Duration
- }
-)
-
-// ServeDNS implements the middleware.Handle interface.
-func (a Auto) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- state := request.Request{W: w, Req: r}
- qname := state.Name()
-
- // TODO(miek): match the qname better in the map
-
- // Precheck with the origins, i.e. are we allowed to looks here.
- zone := middleware.Zones(a.Zones.Origins()).Matches(qname)
- if zone == "" {
- return middleware.NextOrFailure(a.Name(), a.Next, ctx, w, r)
- }
-
- // Now the real zone.
- zone = middleware.Zones(a.Zones.Names()).Matches(qname)
-
- a.Zones.RLock()
- z, ok := a.Zones.Z[zone]
- a.Zones.RUnlock()
-
- if !ok || z == nil {
- return dns.RcodeServerFailure, nil
- }
-
- if state.QType() == dns.TypeAXFR || state.QType() == dns.TypeIXFR {
- xfr := file.Xfr{Zone: z}
- return xfr.ServeDNS(ctx, w, r)
- }
-
- answer, ns, extra, result := z.Lookup(state, qname)
-
- m := new(dns.Msg)
- m.SetReply(r)
- m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
- m.Answer, m.Ns, m.Extra = answer, ns, extra
-
- switch result {
- case file.Success:
- case file.NoData:
- case file.NameError:
- m.Rcode = dns.RcodeNameError
- case file.Delegation:
- m.Authoritative = false
- case file.ServerFailure:
- return dns.RcodeServerFailure, nil
- }
-
- state.SizeAndDo(m)
- m, _ = state.Scrub(m)
- w.WriteMsg(m)
- return dns.RcodeSuccess, nil
-}
-
-// Name implements the Handler interface.
-func (a Auto) Name() string { return "auto" }
diff --git a/middleware/auto/regexp.go b/middleware/auto/regexp.go
deleted file mode 100644
index fa424ec7e..000000000
--- a/middleware/auto/regexp.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package auto
-
-// rewriteToExpand rewrites our template string to one that we can give to regexp.ExpandString. This basically
-// involves prefixing any '{' with a '$'.
-func rewriteToExpand(s string) string {
- // Pretty dumb at the moment, every { will get a $ prefixed.
- // Also wasteful as we build the string with +=. This is OKish
- // as we do this during config parsing.
-
- copy := ""
-
- for _, c := range s {
- if c == '{' {
- copy += "$"
- }
- copy += string(c)
- }
-
- return copy
-}
diff --git a/middleware/auto/regexp_test.go b/middleware/auto/regexp_test.go
deleted file mode 100644
index 17c35eb90..000000000
--- a/middleware/auto/regexp_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package auto
-
-import "testing"
-
-func TestRewriteToExpand(t *testing.T) {
- tests := []struct {
- in string
- expected string
- }{
- {in: "", expected: ""},
- {in: "{1}", expected: "${1}"},
- {in: "{1", expected: "${1"},
- }
- for i, tc := range tests {
- got := rewriteToExpand(tc.in)
- if got != tc.expected {
- t.Errorf("Test %d: Expected error %v, but got %v", i, tc.expected, got)
- }
- }
-}
diff --git a/middleware/auto/setup.go b/middleware/auto/setup.go
deleted file mode 100644
index 426fb93b2..000000000
--- a/middleware/auto/setup.go
+++ /dev/null
@@ -1,172 +0,0 @@
-package auto
-
-import (
- "log"
- "os"
- "path"
- "regexp"
- "strconv"
- "time"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/file"
- "github.com/coredns/coredns/middleware/metrics"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/middleware/proxy"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("auto", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- a, err := autoParse(c)
- if err != nil {
- return middleware.Error("auto", err)
- }
-
- c.OnStartup(func() error {
- m := dnsserver.GetConfig(c).Handler("prometheus")
- if m == nil {
- return nil
- }
- (&a).metrics = m.(*metrics.Metrics)
- return nil
- })
-
- walkChan := make(chan bool)
-
- c.OnStartup(func() error {
- err := a.Walk()
- if err != nil {
- return err
- }
-
- go func() {
- ticker := time.NewTicker(a.loader.duration)
- for {
- select {
- case <-walkChan:
- return
- case <-ticker.C:
- a.Walk()
- }
- }
- }()
- return nil
- })
-
- c.OnShutdown(func() error {
- close(walkChan)
- return nil
- })
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- a.Next = next
- return a
- })
-
- return nil
-}
-
-func autoParse(c *caddy.Controller) (Auto, error) {
- var a = Auto{
- loader: loader{template: "${1}", re: regexp.MustCompile(`db\.(.*)`), duration: 60 * time.Second},
- Zones: &Zones{},
- }
-
- config := dnsserver.GetConfig(c)
-
- for c.Next() {
- // auto [ZONES...]
- a.Zones.origins = make([]string, len(c.ServerBlockKeys))
- copy(a.Zones.origins, c.ServerBlockKeys)
-
- args := c.RemainingArgs()
- if len(args) > 0 {
- a.Zones.origins = args
- }
- for i := range a.Zones.origins {
- a.Zones.origins[i] = middleware.Host(a.Zones.origins[i]).Normalize()
- }
-
- for c.NextBlock() {
- switch c.Val() {
- case "directory": // directory DIR [REGEXP [TEMPLATE] [DURATION]]
- if !c.NextArg() {
- return a, c.ArgErr()
- }
- a.loader.directory = c.Val()
- if !path.IsAbs(a.loader.directory) && config.Root != "" {
- a.loader.directory = path.Join(config.Root, a.loader.directory)
- }
- _, err := os.Stat(a.loader.directory)
- if err != nil {
- if os.IsNotExist(err) {
- log.Printf("[WARNING] Directory does not exist: %s", a.loader.directory)
- } else {
- return a, c.Errf("Unable to access root path '%s': %v", a.loader.directory, err)
- }
- }
-
- // regexp
- if c.NextArg() {
- a.loader.re, err = regexp.Compile(c.Val())
- if err != nil {
- return a, err
- }
- if a.loader.re.NumSubexp() == 0 {
- return a, c.Errf("Need at least one sub expression")
- }
- }
-
- // template
- if c.NextArg() {
- a.loader.template = rewriteToExpand(c.Val())
- }
-
- // duration
- if c.NextArg() {
- i, err := strconv.Atoi(c.Val())
- if err != nil {
- return a, err
- }
- if i < 1 {
- i = 1
- }
- a.loader.duration = time.Duration(i) * time.Second
- }
-
- case "no_reload":
- a.loader.noReload = true
-
- case "upstream":
- args := c.RemainingArgs()
- if len(args) == 0 {
- return a, c.ArgErr()
- }
- ups, err := dnsutil.ParseHostPortOrFile(args...)
- if err != nil {
- return a, err
- }
- a.loader.proxy = proxy.NewLookup(ups)
-
- default:
- t, _, e := file.TransferParse(c, false)
- if e != nil {
- return a, e
- }
- if t != nil {
- a.loader.transferTo = append(a.loader.transferTo, t...)
- }
- }
- }
- }
- return a, nil
-}
diff --git a/middleware/auto/setup_test.go b/middleware/auto/setup_test.go
deleted file mode 100644
index 9754551d2..000000000
--- a/middleware/auto/setup_test.go
+++ /dev/null
@@ -1,125 +0,0 @@
-package auto
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestAutoParse(t *testing.T) {
- tests := []struct {
- inputFileRules string
- shouldErr bool
- expectedDirectory string
- expectedTempl string
- expectedRe string
- expectedTo []string
- }{
- {
- `auto example.org {
- directory /tmp
- transfer to 127.0.0.1
- }`,
- false, "/tmp", "${1}", `db\.(.*)`, []string{"127.0.0.1:53"},
- },
- {
- `auto 10.0.0.0/24 {
- directory /tmp
- }`,
- false, "/tmp", "${1}", `db\.(.*)`, nil,
- },
- {
- `auto {
- directory /tmp
- no_reload
- }`,
- false, "/tmp", "${1}", `db\.(.*)`, nil,
- },
- {
- `auto {
- directory /tmp (.*) bliep
- }`,
- false, "/tmp", "bliep", `(.*)`, nil,
- },
- {
- `auto {
- directory /tmp (.*) bliep 10
- }`,
- false, "/tmp", "bliep", `(.*)`, nil,
- },
- {
- `auto {
- directory /tmp (.*) bliep
- transfer to 127.0.0.1
- transfer to 127.0.0.2
- upstream 8.8.8.8
- }`,
- false, "/tmp", "bliep", `(.*)`, []string{"127.0.0.1:53", "127.0.0.2:53"},
- },
- // errors
- {
- `auto example.org {
- directory
- }`,
- true, "", "${1}", `db\.(.*)`, nil,
- },
- {
- `auto example.org {
- directory /tmp * {1}
- }`,
- true, "", "${1}", ``, nil,
- },
- {
- `auto example.org {
- directory /tmp * {1} aa
- }`,
- true, "", "${1}", ``, nil,
- },
- {
- `auto example.org {
- directory /tmp .* {1}
- }`,
- true, "", "${1}", ``, nil,
- },
- {
- `auto example.org {
- directory /tmp .* {1}
- }`,
- true, "", "${1}", ``, nil,
- },
- {
- `auto example.org {
- directory /tmp .* {1}
- }`,
- true, "", "${1}", ``, nil,
- },
- }
-
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.inputFileRules)
- a, err := autoParse(c)
-
- if err == nil && test.shouldErr {
- t.Fatalf("Test %d expected errors, but got no error", i)
- } else if err != nil && !test.shouldErr {
- t.Fatalf("Test %d expected no errors, but got '%v'", i, err)
- } else if !test.shouldErr {
- if a.loader.directory != test.expectedDirectory {
- t.Fatalf("Test %d expected %v, got %v", i, test.expectedDirectory, a.loader.directory)
- }
- if a.loader.template != test.expectedTempl {
- t.Fatalf("Test %d expected %v, got %v", i, test.expectedTempl, a.loader.template)
- }
- if a.loader.re.String() != test.expectedRe {
- t.Fatalf("Test %d expected %v, got %v", i, test.expectedRe, a.loader.re)
- }
- if test.expectedTo != nil {
- for j, got := range a.loader.transferTo {
- if got != test.expectedTo[j] {
- t.Fatalf("Test %d expected %v, got %v", i, test.expectedTo[j], got)
- }
- }
- }
- }
- }
-}
diff --git a/middleware/auto/walk.go b/middleware/auto/walk.go
deleted file mode 100644
index 1bb351691..000000000
--- a/middleware/auto/walk.go
+++ /dev/null
@@ -1,109 +0,0 @@
-package auto
-
-import (
- "log"
- "os"
- "path"
- "path/filepath"
- "regexp"
-
- "github.com/coredns/coredns/middleware/file"
-
- "github.com/miekg/dns"
-)
-
-// Walk will recursively walk of the file under l.directory and adds the one that match l.re.
-func (a Auto) Walk() error {
-
- // TODO(miek): should add something so that we don't stomp on each other.
-
- toDelete := make(map[string]bool)
- for _, n := range a.Zones.Names() {
- toDelete[n] = true
- }
-
- filepath.Walk(a.loader.directory, func(path string, info os.FileInfo, err error) error {
- if info == nil || info.IsDir() {
- return nil
- }
-
- match, origin := matches(a.loader.re, info.Name(), a.loader.template)
- if !match {
- return nil
- }
-
- if _, ok := a.Zones.Z[origin]; ok {
- // we already have this zone
- toDelete[origin] = false
- return nil
- }
-
- reader, err := os.Open(path)
- if err != nil {
- log.Printf("[WARNING] Opening %s failed: %s", path, err)
- return nil
- }
- defer reader.Close()
-
- // Serial for loading a zone is 0, because it is a new zone.
- zo, err := file.Parse(reader, origin, path, 0)
- if err != nil {
- log.Printf("[WARNING] Parse zone `%s': %v", origin, err)
- return nil
- }
-
- zo.NoReload = a.loader.noReload
- zo.Proxy = a.loader.proxy
- zo.TransferTo = a.loader.transferTo
-
- a.Zones.Add(zo, origin)
-
- if a.metrics != nil {
- a.metrics.AddZone(origin)
- }
-
- zo.Notify()
-
- log.Printf("[INFO] Inserting zone `%s' from: %s", origin, path)
-
- toDelete[origin] = false
-
- return nil
- })
-
- for origin, ok := range toDelete {
- if !ok {
- continue
- }
-
- if a.metrics != nil {
- a.metrics.RemoveZone(origin)
- }
-
- a.Zones.Remove(origin)
-
- log.Printf("[INFO] Deleting zone `%s'", origin)
- }
-
- return nil
-}
-
-// matches matches re to filename, if is is a match, the subexpression will be used to expand
-// template to an origin. When match is true that origin is returned. Origin is fully qualified.
-func matches(re *regexp.Regexp, filename, template string) (match bool, origin string) {
- base := path.Base(filename)
-
- matches := re.FindStringSubmatchIndex(base)
- if matches == nil {
- return false, ""
- }
-
- by := re.ExpandString(nil, template, base, matches)
- if by == nil {
- return false, ""
- }
-
- origin = dns.Fqdn(string(by))
-
- return true, origin
-}
diff --git a/middleware/auto/walk_test.go b/middleware/auto/walk_test.go
deleted file mode 100644
index 29b9dbb55..000000000
--- a/middleware/auto/walk_test.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package auto
-
-import (
- "io/ioutil"
- "log"
- "os"
- "path"
- "regexp"
- "testing"
-)
-
-var dbFiles = []string{"db.example.org", "aa.example.org"}
-
-const zoneContent = `; testzone
-@ IN SOA sns.dns.icann.org. noc.dns.icann.org. 2016082534 7200 3600 1209600 3600
- NS a.iana-servers.net.
- NS b.iana-servers.net.
-
-www IN A 127.0.0.1
-`
-
-func TestWalk(t *testing.T) {
- log.SetOutput(ioutil.Discard)
-
- tempdir, err := createFiles()
- if err != nil {
- if tempdir != "" {
- os.RemoveAll(tempdir)
- }
- t.Fatal(err)
- }
- defer os.RemoveAll(tempdir)
-
- ldr := loader{
- directory: tempdir,
- re: regexp.MustCompile(`db\.(.*)`),
- template: `${1}`,
- }
-
- a := Auto{
- loader: ldr,
- Zones: &Zones{},
- }
-
- a.Walk()
-
- // db.example.org and db.example.com should be here (created in createFiles)
- for _, name := range []string{"example.com.", "example.org."} {
- if _, ok := a.Zones.Z[name]; !ok {
- t.Errorf("%s should have been added", name)
- }
- }
-}
-
-func TestWalkNonExistent(t *testing.T) {
- log.SetOutput(ioutil.Discard)
-
- nonExistingDir := "highly_unlikely_to_exist_dir"
-
- ldr := loader{
- directory: nonExistingDir,
- re: regexp.MustCompile(`db\.(.*)`),
- template: `${1}`,
- }
-
- a := Auto{
- loader: ldr,
- Zones: &Zones{},
- }
-
- a.Walk()
-}
-
-func createFiles() (string, error) {
- dir, err := ioutil.TempDir(os.TempDir(), "coredns")
- if err != nil {
- return dir, err
- }
-
- for _, name := range dbFiles {
- if err := ioutil.WriteFile(path.Join(dir, name), []byte(zoneContent), 0644); err != nil {
- return dir, err
- }
- }
- // symlinks
- if err = os.Symlink(path.Join(dir, "db.example.org"), path.Join(dir, "db.example.com")); err != nil {
- return dir, err
- }
- if err = os.Symlink(path.Join(dir, "db.example.org"), path.Join(dir, "aa.example.com")); err != nil {
- return dir, err
- }
-
- return dir, nil
-}
diff --git a/middleware/auto/watcher_test.go b/middleware/auto/watcher_test.go
deleted file mode 100644
index 329d8dc85..000000000
--- a/middleware/auto/watcher_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package auto
-
-import (
- "io/ioutil"
- "log"
- "os"
- "path"
- "regexp"
- "testing"
-)
-
-func TestWatcher(t *testing.T) {
- log.SetOutput(ioutil.Discard)
-
- tempdir, err := createFiles()
- if err != nil {
- if tempdir != "" {
- os.RemoveAll(tempdir)
- }
- t.Fatal(err)
- }
- defer os.RemoveAll(tempdir)
-
- ldr := loader{
- directory: tempdir,
- re: regexp.MustCompile(`db\.(.*)`),
- template: `${1}`,
- }
-
- a := Auto{
- loader: ldr,
- Zones: &Zones{},
- }
-
- a.Walk()
-
- // example.org and example.com should exist
- if x := len(a.Zones.Z["example.org."].All()); x != 4 {
- t.Fatalf("Expected 4 RRs, got %d", x)
- }
- if x := len(a.Zones.Z["example.com."].All()); x != 4 {
- t.Fatalf("Expected 4 RRs, got %d", x)
- }
-
- // Now remove one file, rescan and see if it's gone.
- if err := os.Remove(path.Join(tempdir, "db.example.com")); err != nil {
- t.Fatal(err)
- }
-
- a.Walk()
-
- if _, ok := a.Zones.Z["example.com."]; ok {
- t.Errorf("Expected %q to be gone.", "example.com.")
- }
- if _, ok := a.Zones.Z["example.org."]; !ok {
- t.Errorf("Expected %q to still be there.", "example.org.")
- }
-}
diff --git a/middleware/auto/zone.go b/middleware/auto/zone.go
deleted file mode 100644
index ead26541e..000000000
--- a/middleware/auto/zone.go
+++ /dev/null
@@ -1,76 +0,0 @@
-// Package auto implements a on-the-fly loading file backend.
-package auto
-
-import (
- "sync"
-
- "github.com/coredns/coredns/middleware/file"
-)
-
-// Zones maps zone names to a *Zone. This keep track of what we zones we have loaded at
-// any one time.
-type Zones struct {
- Z map[string]*file.Zone // A map mapping zone (origin) to the Zone's data.
- names []string // All the keys from the map Z as a string slice.
-
- origins []string // Any origins from the server block.
-
- sync.RWMutex
-}
-
-// Names returns the names from z.
-func (z *Zones) Names() []string {
- z.RLock()
- n := z.names
- z.RUnlock()
- return n
-}
-
-// Origins returns the origins from z.
-func (z *Zones) Origins() []string {
- // doesn't need locking, because there aren't multiple Go routines accessing it.
- return z.origins
-}
-
-// Zones returns a zone with origin name from z, nil when not found.
-func (z *Zones) Zones(name string) *file.Zone {
- z.RLock()
- zo := z.Z[name]
- z.RUnlock()
- return zo
-}
-
-// Add adds a new zone into z. If zo.NoReload is false, the
-// reload goroutine is started.
-func (z *Zones) Add(zo *file.Zone, name string) {
- z.Lock()
-
- if z.Z == nil {
- z.Z = make(map[string]*file.Zone)
- }
-
- z.Z[name] = zo
- z.names = append(z.names, name)
- zo.Reload()
-
- z.Unlock()
-}
-
-// Remove removes the zone named name from z. It also stop the the zone's reload goroutine.
-func (z *Zones) Remove(name string) {
- z.Lock()
-
- if zo, ok := z.Z[name]; ok && !zo.NoReload {
- zo.ReloadShutdown <- true
- }
-
- delete(z.Z, name)
-
- // TODO(miek): just regenerate Names (might be bad if you have a lot of zones...)
- z.names = []string{}
- for n := range z.Z {
- z.names = append(z.names, n)
- }
-
- z.Unlock()
-}
diff --git a/middleware/autopath/README.md b/middleware/autopath/README.md
deleted file mode 100644
index 582b11f4e..000000000
--- a/middleware/autopath/README.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# autopath
-
-The *autopath* middleware allows CoreDNS to perform server side search path completion.
-If it sees a query that matches the first element of the configured search path, *autopath* will
-follow the chain of search path elements and returns the first reply that is not NXDOMAIN.
-On any failures the original reply is returned.
-
-Because *autopath* returns a reply for a name that wasn't the original question it will add a CNAME
-that points from the original name (with the search path element in it) to the name of this answer.
-
-## Syntax
-
-~~~
-autopath [ZONE..] RESOLV-CONF
-~~~
-
-* **ZONES** zones *autopath* should be authoritative for.
-* **RESOLV-CONF** points to a `resolv.conf` like file or uses a special syntax to point to another
- middleware. For instance `@kubernetes`, will call out to the kubernetes middleware (for each
- query) to retrieve the search list it should use.
-
-Currently the following set of middleware has implemented *autopath*:
-
-* *kubernetes*
-* *erratic*
-
-## Examples
-
-~~~
-autopath my-resolv.conf
-~~~
-
-Use `my-resolv.conf` as the file to get the search path from. This file only needs so have one line:
-`search domain1 domain2 ...`
-
-~~~
-autopath @kubernetes
-~~~
-
-Use the search path dynamically retrieved from the kubernetes middleware.
-
-## Bugs
-
-When the *cache* middleware is enabled it is possible for pods in different namespaces to get the
-same answer.
diff --git a/middleware/autopath/autopath.go b/middleware/autopath/autopath.go
deleted file mode 100644
index 94db8bc56..000000000
--- a/middleware/autopath/autopath.go
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
-Package autopath implements autopathing. This is a hack; it shortcuts the
-client's search path resolution by performing these lookups on the server...
-
-The server has a copy (via AutoPathFunc) of the client's search path and on
-receiving a query it first establish if the suffix matches the FIRST configured
-element. If no match can be found the query will be forwarded up the middleware
-chain without interference (iff 'fallthrough' has been set).
-
-If the query is deemed to fall in the search path the server will perform the
-queries with each element of the search path appended in sequence until a
-non-NXDOMAIN answer has been found. That reply will then be returned to the
-client - with some CNAME hackery to let the client accept the reply.
-
-If all queries return NXDOMAIN we return the original as-is and let the client
-continue searching. The client will go to the next element in the search path,
-but we won’t do any more autopathing. It means that in the failure case, you do
-more work, since the server looks it up, then the client still needs to go
-through the search path.
-
-It is assume the search path ordering is identical between server and client.
-
-Midldeware implementing autopath, must have a function called `AutoPath` of type
-autopath.Func. Note the searchpath must be ending with the empty string.
-
-I.e:
-
-func (m Middleware ) AutoPath(state request.Request) []string {
- return []string{"first", "second", "last", ""}
-}
-*/
-package autopath
-
-import (
- "log"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/middleware/pkg/nonwriter"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// Func defines the function middleware should implement to return a search
-// path to the autopath middleware. The last element of the slice must be the empty string.
-// If Func returns a nil slice, no autopathing will be done.
-type Func func(request.Request) []string
-
-// AutoPath perform autopath: service side search path completion.
-type AutoPath struct {
- Next middleware.Handler
- Zones []string
-
- // Search always includes "" as the last element, so we try the base query with out any search paths added as well.
- search []string
- searchFunc Func
-}
-
-// ServeDNS implements the middleware.Handle interface.
-func (a *AutoPath) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- state := request.Request{W: w, Req: r}
-
- zone := middleware.Zones(a.Zones).Matches(state.Name())
- if zone == "" {
- return middleware.NextOrFailure(a.Name(), a.Next, ctx, w, r)
- }
-
- // Check if autopath should be done, searchFunc takes precedence over the local configured search path.
- var err error
- searchpath := a.search
-
- if a.searchFunc != nil {
- searchpath = a.searchFunc(state)
- }
-
- if len(searchpath) == 0 {
- log.Printf("[WARNING] No search path available for autopath")
- return middleware.NextOrFailure(a.Name(), a.Next, ctx, w, r)
- }
-
- if !firstInSearchPath(state.Name(), searchpath) {
- return middleware.NextOrFailure(a.Name(), a.Next, ctx, w, r)
- }
-
- origQName := state.QName()
-
- // Establish base name of the query. I.e what was originally asked.
- base, err := dnsutil.TrimZone(state.QName(), searchpath[0]) // TODO(miek): we loose the original case of the query here.
- if err != nil {
- return dns.RcodeServerFailure, err
- }
-
- firstReply := new(dns.Msg)
- firstRcode := 0
- var firstErr error
-
- ar := r.Copy()
- // Walk the search path and see if we can get a non-nxdomain - if they all fail we return the first
- // query we've done and return that as-is. This means the client will do the search path walk again...
- for i, s := range searchpath {
- newQName := base + "." + s
- ar.Question[0].Name = newQName
- nw := nonwriter.New(w)
-
- rcode, err := middleware.NextOrFailure(a.Name(), a.Next, ctx, nw, ar)
- if err != nil {
- // Return now - not sure if this is the best. We should also check if the write has happened.
- return rcode, err
- }
- if i == 0 {
- firstReply = nw.Msg
- firstRcode = rcode
- firstErr = err
- }
-
- if !middleware.ClientWrite(rcode) {
- continue
- }
-
- if nw.Msg.Rcode == dns.RcodeNameError {
- continue
- }
-
- msg := nw.Msg
- cnamer(msg, origQName)
-
- // Write whatever non-nxdomain answer we've found.
- w.WriteMsg(msg)
- return rcode, err
-
- }
- if middleware.ClientWrite(firstRcode) {
- w.WriteMsg(firstReply)
- }
- return firstRcode, firstErr
-}
-
-// Name implements the Handler interface.
-func (a *AutoPath) Name() string { return "autopath" }
-
-// firstInSearchPath checks if name is equal to are a sibling of the first element in the search path.
-func firstInSearchPath(name string, searchpath []string) bool {
- if name == searchpath[0] {
- return true
- }
- if dns.IsSubDomain(searchpath[0], name) {
- return true
- }
- return false
-}
diff --git a/middleware/autopath/autopath_test.go b/middleware/autopath/autopath_test.go
deleted file mode 100644
index b99744ab8..000000000
--- a/middleware/autopath/autopath_test.go
+++ /dev/null
@@ -1,166 +0,0 @@
-package autopath
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-var autopathTestCases = []test.Case{
- {
- // search path expansion.
- Qname: "b.example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.CNAME("b.example.org. 3600 IN CNAME b.com."),
- test.A("b.com." + defaultA),
- },
- },
- {
- // No search path expansion
- Qname: "a.example.com.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.A("a.example.com." + defaultA),
- },
- },
-}
-
-func newTestAutoPath() *AutoPath {
- ap := new(AutoPath)
- ap.Zones = []string{"."}
- ap.Next = nextHandler(map[string]int{
- "b.example.org.": dns.RcodeNameError,
- "b.com.": dns.RcodeSuccess,
- "a.example.com.": dns.RcodeSuccess,
- })
-
- ap.search = []string{"example.org.", "example.com.", "com.", ""}
- return ap
-}
-
-func TestAutoPath(t *testing.T) {
- ap := newTestAutoPath()
- ctx := context.TODO()
-
- for _, tc := range autopathTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := ap.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("expected no error, got %v\n", err)
- continue
- }
-
- // No sorting here as we want to check if the CNAME sits *before* the
- // test of the answer.
- resp := rec.Msg
-
- if !test.Header(t, tc, resp) {
- t.Logf("%v\n", resp)
- continue
- }
- if !test.Section(t, tc, test.Answer, resp.Answer) {
- t.Logf("%v\n", resp)
- }
- if !test.Section(t, tc, test.Ns, resp.Ns) {
- t.Logf("%v\n", resp)
- }
- if !test.Section(t, tc, test.Extra, resp.Extra) {
- t.Logf("%v\n", resp)
- }
- }
-}
-
-var autopathNoAnswerTestCases = []test.Case{
- {
- // search path expansion, no answer
- Qname: "c.example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.CNAME("b.example.org. 3600 IN CNAME b.com."),
- test.A("b.com." + defaultA),
- },
- },
-}
-
-func TestAutoPathNoAnswer(t *testing.T) {
- ap := newTestAutoPath()
- ctx := context.TODO()
-
- for _, tc := range autopathNoAnswerTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- rcode, err := ap.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("expected no error, got %v\n", err)
- continue
- }
- if middleware.ClientWrite(rcode) {
- t.Fatalf("expected no client write, got one for rcode %d", rcode)
- }
- }
-}
-
-// nextHandler returns a Handler that returns an answer for the question in the
-// request per the domain->answer map. On success an RR will be returned: "qname 3600 IN A 127.0.0.53"
-func nextHandler(mm map[string]int) test.Handler {
- return test.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- rcode, ok := mm[r.Question[0].Name]
- if !ok {
- return dns.RcodeServerFailure, nil
- }
-
- m := new(dns.Msg)
- m.SetReply(r)
-
- switch rcode {
- case dns.RcodeNameError:
- m.Rcode = rcode
- m.Ns = []dns.RR{soa}
- w.WriteMsg(m)
- return m.Rcode, nil
-
- case dns.RcodeSuccess:
- m.Rcode = rcode
- a, _ := dns.NewRR(r.Question[0].Name + defaultA)
- m.Answer = []dns.RR{a}
-
- w.WriteMsg(m)
- return m.Rcode, nil
- default:
- panic("nextHandler: unhandled rcode")
- }
- })
-}
-
-const defaultA = " 3600 IN A 127.0.0.53"
-
-var soa = func() dns.RR {
- s, _ := dns.NewRR("example.org. 1800 IN SOA example.org. example.org. 1502165581 14400 3600 604800 14400")
- return s
-}()
-
-func TestInSearchPath(t *testing.T) {
- a := AutoPath{search: []string{"default.svc.cluster.local.", "svc.cluster.local.", "cluster.local."}}
-
- tests := []struct {
- qname string
- b bool
- }{
- {"google.com", false},
- {"default.svc.cluster.local.", true},
- {"a.default.svc.cluster.local.", true},
- {"a.b.svc.cluster.local.", false},
- }
- for i, tc := range tests {
- got := firstInSearchPath(tc.qname, a.search)
- if got != tc.b {
- t.Errorf("Test %d, got %v, expected %v", i, got, tc.b)
- }
- }
-}
diff --git a/middleware/autopath/cname.go b/middleware/autopath/cname.go
deleted file mode 100644
index 3b2c60f4e..000000000
--- a/middleware/autopath/cname.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package autopath
-
-import (
- "strings"
-
- "github.com/miekg/dns"
-)
-
-// cnamer will prefix the answer section with a cname that points from original qname to the
-// name of the first RR. It will also update the question section and put original in there.
-func cnamer(m *dns.Msg, original string) {
- for _, a := range m.Answer {
- if strings.EqualFold(original, a.Header().Name) {
- continue
- }
- m.Answer = append(m.Answer, nil)
- copy(m.Answer[1:], m.Answer)
- m.Answer[0] = &dns.CNAME{
- Hdr: dns.RR_Header{Name: original, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: a.Header().Ttl},
- Target: a.Header().Name,
- }
- break
- }
- m.Question[0].Name = original
-}
diff --git a/middleware/autopath/setup.go b/middleware/autopath/setup.go
deleted file mode 100644
index 368d92cf4..000000000
--- a/middleware/autopath/setup.go
+++ /dev/null
@@ -1,93 +0,0 @@
-package autopath
-
-import (
- "fmt"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/erratic"
- "github.com/coredns/coredns/middleware/kubernetes"
-
- "github.com/mholt/caddy"
- "github.com/miekg/dns"
-)
-
-func init() {
- caddy.RegisterPlugin("autopath", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-
-}
-
-func setup(c *caddy.Controller) error {
- ap, mw, err := autoPathParse(c)
- if err != nil {
- return middleware.Error("autopath", err)
- }
-
- // Do this in OnStartup, so all middleware has been initialized.
- c.OnStartup(func() error {
- m := dnsserver.GetConfig(c).Handler(mw)
- if m == nil {
- return nil
- }
- if x, ok := m.(*kubernetes.Kubernetes); ok {
- ap.searchFunc = x.AutoPath
- }
- if x, ok := m.(*erratic.Erratic); ok {
- ap.searchFunc = x.AutoPath
- }
- return nil
- })
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- ap.Next = next
- return ap
- })
-
- return nil
-}
-
-// allowedMiddleware has a list of middleware that can be used by autopath.
-var allowedMiddleware = map[string]bool{
- "@kubernetes": true,
- "@erratic": true,
-}
-
-func autoPathParse(c *caddy.Controller) (*AutoPath, string, error) {
- ap := &AutoPath{}
- mw := ""
-
- for c.Next() {
- zoneAndresolv := c.RemainingArgs()
- if len(zoneAndresolv) < 1 {
- return ap, "", fmt.Errorf("no resolv-conf specified")
- }
- resolv := zoneAndresolv[len(zoneAndresolv)-1]
- if resolv[0] == '@' {
- _, ok := allowedMiddleware[resolv]
- if ok {
- mw = resolv[1:]
- }
- } else {
- // assume file on disk
- rc, err := dns.ClientConfigFromFile(resolv)
- if err != nil {
- return ap, "", fmt.Errorf("failed to parse %q: %v", resolv, err)
- }
- ap.search = rc.Search
- middleware.Zones(ap.search).Normalize()
- ap.search = append(ap.search, "") // sentinal value as demanded.
- }
- ap.Zones = zoneAndresolv[:len(zoneAndresolv)-1]
- if len(ap.Zones) == 0 {
- ap.Zones = make([]string, len(c.ServerBlockKeys))
- copy(ap.Zones, c.ServerBlockKeys)
- }
- for i, str := range ap.Zones {
- ap.Zones[i] = middleware.Host(str).Normalize()
- }
- }
- return ap, mw, nil
-}
diff --git a/middleware/autopath/setup_test.go b/middleware/autopath/setup_test.go
deleted file mode 100644
index 0f086c5bb..000000000
--- a/middleware/autopath/setup_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package autopath
-
-import (
- "os"
- "reflect"
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/mholt/caddy"
-)
-
-func TestSetupAutoPath(t *testing.T) {
- resolv, rm, err := test.TempFile(os.TempDir(), resolvConf)
- if err != nil {
- t.Fatalf("Could not create resolv.conf test file %s: %s", resolvConf, err)
- }
- defer rm()
-
- tests := []struct {
- input string
- shouldErr bool
- expectedZone string
- expectedMw string // expected middleware.
- expectedSearch []string // expected search path
- expectedErrContent string // substring from the expected error. Empty for positive cases.
- }{
- // positive
- {`autopath @kubernetes`, false, "", "kubernetes", nil, ""},
- {`autopath example.org @kubernetes`, false, "example.org.", "kubernetes", nil, ""},
- {`autopath 10.0.0.0/8 @kubernetes`, false, "10.in-addr.arpa.", "kubernetes", nil, ""},
- {`autopath ` + resolv, false, "", "", []string{"bar.com.", "baz.com.", ""}, ""},
- // negative
- {`autopath kubernetes`, true, "", "", nil, "open kubernetes: no such file or directory"},
- {`autopath`, true, "", "", nil, "no resolv-conf"},
- }
-
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- ap, mw, err := autoPathParse(c)
-
- if test.shouldErr && err == nil {
- t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input)
- }
-
- if err != nil {
- if !test.shouldErr {
- t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
- }
-
- if !strings.Contains(err.Error(), test.expectedErrContent) {
- t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input)
- }
- }
-
- if !test.shouldErr && mw != test.expectedMw {
- t.Errorf("Test %d, Middleware not correctly set for input %s. Expected: %s, actual: %s", i, test.input, test.expectedMw, mw)
- }
- if !test.shouldErr && ap.search != nil {
- if !reflect.DeepEqual(test.expectedSearch, ap.search) {
- t.Errorf("Test %d, wrong searchpath for input %s. Expected: '%v', actual: '%v'", i, test.input, test.expectedSearch, ap.search)
- }
- }
- if !test.shouldErr && test.expectedZone != "" {
- if test.expectedZone != ap.Zones[0] {
- t.Errorf("Test %d, expected zone %q for input %s, got: %q", i, test.expectedZone, test.input, ap.Zones[0])
- }
- }
- }
-}
-
-const resolvConf = `nameserver 1.2.3.4
-domain foo.com
-search bar.com baz.com
-options ndots:5
-`
diff --git a/middleware/backend.go b/middleware/backend.go
deleted file mode 100644
index bf4bf8967..000000000
--- a/middleware/backend.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package middleware
-
-import (
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-// ServiceBackend defines a (dynamic) backend that returns a slice of service definitions.
-type ServiceBackend interface {
- // Services communicates with the backend to retrieve the service definition. Exact indicates
- // on exact much are that we are allowed to recurs.
- Services(state request.Request, exact bool, opt Options) ([]msg.Service, error)
-
- // Reverse communicates with the backend to retrieve service definition based on a IP address
- // instead of a name. I.e. a reverse DNS lookup.
- Reverse(state request.Request, exact bool, opt Options) ([]msg.Service, error)
-
- // Lookup is used to find records else where.
- Lookup(state request.Request, name string, typ uint16) (*dns.Msg, error)
-
- // Returns _all_ services that matches a certain name.
- // Note: it does not implement a specific service.
- Records(state request.Request, exact bool) ([]msg.Service, error)
-
- // IsNameError return true if err indicated a record not found condition
- IsNameError(err error) bool
-}
-
-// Options are extra options that can be specified for a lookup.
-type Options struct{}
diff --git a/middleware/backend_lookup.go b/middleware/backend_lookup.go
deleted file mode 100644
index 9e451feed..000000000
--- a/middleware/backend_lookup.go
+++ /dev/null
@@ -1,410 +0,0 @@
-package middleware
-
-import (
- "fmt"
- "math"
- "net"
- "time"
-
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-// A returns A records from Backend or an error.
-func A(b ServiceBackend, zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, err error) {
- services, err := b.Services(state, false, opt)
- if err != nil {
- return nil, err
- }
-
- for _, serv := range services {
-
- what, ip := serv.HostType()
-
- switch what {
- case dns.TypeCNAME:
- if Name(state.Name()).Matches(dns.Fqdn(serv.Host)) {
- // x CNAME x is a direct loop, don't add those
- continue
- }
-
- newRecord := serv.NewCNAME(state.QName(), serv.Host)
- if len(previousRecords) > 7 {
- // don't add it, and just continue
- continue
- }
- if dnsutil.DuplicateCNAME(newRecord, previousRecords) {
- continue
- }
-
- state1 := state.NewWithQuestion(serv.Host, state.QType())
- nextRecords, err := A(b, zone, state1, append(previousRecords, newRecord), opt)
-
- if err == nil {
- // Not only have we found something we should add the CNAME and the IP addresses.
- if len(nextRecords) > 0 {
- records = append(records, newRecord)
- records = append(records, nextRecords...)
- }
- continue
- }
- // This means we can not complete the CNAME, try to look else where.
- target := newRecord.Target
- if dns.IsSubDomain(zone, target) {
- // We should already have found it
- continue
- }
- // Lookup
- m1, e1 := b.Lookup(state, target, state.QType())
- if e1 != nil {
- continue
- }
- // Len(m1.Answer) > 0 here is well?
- records = append(records, newRecord)
- records = append(records, m1.Answer...)
- continue
-
- case dns.TypeA:
- records = append(records, serv.NewA(state.QName(), ip))
-
- case dns.TypeAAAA:
- // nodata?
- }
- }
- return records, nil
-}
-
-// AAAA returns AAAA records from Backend or an error.
-func AAAA(b ServiceBackend, zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, err error) {
- services, err := b.Services(state, false, opt)
- if err != nil {
- return nil, err
- }
-
- for _, serv := range services {
-
- what, ip := serv.HostType()
-
- switch what {
- case dns.TypeCNAME:
- // Try to resolve as CNAME if it's not an IP, but only if we don't create loops.
- if Name(state.Name()).Matches(dns.Fqdn(serv.Host)) {
- // x CNAME x is a direct loop, don't add those
- continue
- }
-
- newRecord := serv.NewCNAME(state.QName(), serv.Host)
- if len(previousRecords) > 7 {
- // don't add it, and just continue
- continue
- }
- if dnsutil.DuplicateCNAME(newRecord, previousRecords) {
- continue
- }
-
- state1 := state.NewWithQuestion(serv.Host, state.QType())
- nextRecords, err := AAAA(b, zone, state1, append(previousRecords, newRecord), opt)
-
- if err == nil {
- // Not only have we found something we should add the CNAME and the IP addresses.
- if len(nextRecords) > 0 {
- records = append(records, newRecord)
- records = append(records, nextRecords...)
- }
- continue
- }
- // This means we can not complete the CNAME, try to look else where.
- target := newRecord.Target
- if dns.IsSubDomain(zone, target) {
- // We should already have found it
- continue
- }
- m1, e1 := b.Lookup(state, target, state.QType())
- if e1 != nil {
- continue
- }
- // Len(m1.Answer) > 0 here is well?
- records = append(records, newRecord)
- records = append(records, m1.Answer...)
- continue
- // both here again
-
- case dns.TypeA:
- // nada?
-
- case dns.TypeAAAA:
- records = append(records, serv.NewAAAA(state.QName(), ip))
- }
- }
- return records, nil
-}
-
-// SRV returns SRV records from the Backend.
-// If the Target is not a name but an IP address, a name is created on the fly.
-func SRV(b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) {
- services, err := b.Services(state, false, opt)
- if err != nil {
- return nil, nil, err
- }
-
- // Looping twice to get the right weight vs priority
- w := make(map[int]int)
- for _, serv := range services {
- weight := 100
- if serv.Weight != 0 {
- weight = serv.Weight
- }
- if _, ok := w[serv.Priority]; !ok {
- w[serv.Priority] = weight
- continue
- }
- w[serv.Priority] += weight
- }
- lookup := make(map[string]bool)
- for _, serv := range services {
- w1 := 100.0 / float64(w[serv.Priority])
- if serv.Weight == 0 {
- w1 *= 100
- } else {
- w1 *= float64(serv.Weight)
- }
- weight := uint16(math.Floor(w1))
-
- what, ip := serv.HostType()
-
- switch what {
- case dns.TypeCNAME:
- srv := serv.NewSRV(state.QName(), weight)
- records = append(records, srv)
-
- if _, ok := lookup[srv.Target]; ok {
- break
- }
-
- lookup[srv.Target] = true
-
- if !dns.IsSubDomain(zone, srv.Target) {
- m1, e1 := b.Lookup(state, srv.Target, dns.TypeA)
- if e1 == nil {
- extra = append(extra, m1.Answer...)
- }
-
- m1, e1 = b.Lookup(state, srv.Target, dns.TypeAAAA)
- if e1 == nil {
- // If we have seen CNAME's we *assume* that they are already added.
- for _, a := range m1.Answer {
- if _, ok := a.(*dns.CNAME); !ok {
- extra = append(extra, a)
- }
- }
- }
- break
- }
- // Internal name, we should have some info on them, either v4 or v6
- // Clients expect a complete answer, because we are a recursor in their view.
- state1 := state.NewWithQuestion(srv.Target, dns.TypeA)
- addr, e1 := A(b, zone, state1, nil, opt)
- if e1 == nil {
- extra = append(extra, addr...)
- }
- // IPv6 lookups here as well? AAAA(zone, state1, nil).
-
- case dns.TypeA, dns.TypeAAAA:
- serv.Host = msg.Domain(serv.Key)
- srv := serv.NewSRV(state.QName(), weight)
-
- records = append(records, srv)
- extra = append(extra, newAddress(serv, srv.Target, ip, what))
- }
- }
- return records, extra, nil
-}
-
-// MX returns MX records from the Backend. If the Target is not a name but an IP address, a name is created on the fly.
-func MX(b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) {
- services, err := b.Services(state, false, opt)
- if err != nil {
- return nil, nil, err
- }
-
- lookup := make(map[string]bool)
- for _, serv := range services {
- if !serv.Mail {
- continue
- }
- what, ip := serv.HostType()
- switch what {
- case dns.TypeCNAME:
- mx := serv.NewMX(state.QName())
- records = append(records, mx)
- if _, ok := lookup[mx.Mx]; ok {
- break
- }
-
- lookup[mx.Mx] = true
-
- if !dns.IsSubDomain(zone, mx.Mx) {
- m1, e1 := b.Lookup(state, mx.Mx, dns.TypeA)
- if e1 == nil {
- extra = append(extra, m1.Answer...)
- }
-
- m1, e1 = b.Lookup(state, mx.Mx, dns.TypeAAAA)
- if e1 == nil {
- // If we have seen CNAME's we *assume* that they are already added.
- for _, a := range m1.Answer {
- if _, ok := a.(*dns.CNAME); !ok {
- extra = append(extra, a)
- }
- }
- }
- break
- }
- // Internal name
- state1 := state.NewWithQuestion(mx.Mx, dns.TypeA)
- addr, e1 := A(b, zone, state1, nil, opt)
- if e1 == nil {
- extra = append(extra, addr...)
- }
- // e.AAAA as well
-
- case dns.TypeA, dns.TypeAAAA:
- serv.Host = msg.Domain(serv.Key)
- records = append(records, serv.NewMX(state.QName()))
- extra = append(extra, newAddress(serv, serv.Host, ip, what))
- }
- }
- return records, extra, nil
-}
-
-// CNAME returns CNAME records from the backend or an error.
-func CNAME(b ServiceBackend, zone string, state request.Request, opt Options) (records []dns.RR, err error) {
- services, err := b.Services(state, true, opt)
- if err != nil {
- return nil, err
- }
-
- if len(services) > 0 {
- serv := services[0]
- if ip := net.ParseIP(serv.Host); ip == nil {
- records = append(records, serv.NewCNAME(state.QName(), serv.Host))
- }
- }
- return records, nil
-}
-
-// TXT returns TXT records from Backend or an error.
-func TXT(b ServiceBackend, zone string, state request.Request, opt Options) (records []dns.RR, err error) {
- services, err := b.Services(state, false, opt)
- if err != nil {
- return nil, err
- }
-
- for _, serv := range services {
- if serv.Text == "" {
- continue
- }
- records = append(records, serv.NewTXT(state.QName()))
- }
- return records, nil
-}
-
-// PTR returns the PTR records from the backend, only services that have a domain name as host are included.
-func PTR(b ServiceBackend, zone string, state request.Request, opt Options) (records []dns.RR, err error) {
- services, err := b.Reverse(state, true, opt)
- if err != nil {
- return nil, err
- }
-
- for _, serv := range services {
- if ip := net.ParseIP(serv.Host); ip == nil {
- records = append(records, serv.NewPTR(state.QName(), serv.Host))
- }
- }
- return records, nil
-}
-
-// NS returns NS records from the backend
-func NS(b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) {
- // NS record for this zone live in a special place, ns.dns.<zone>. Fake our lookup.
- // only a tad bit fishy...
- old := state.QName()
-
- state.Clear()
- state.Req.Question[0].Name = "ns.dns." + zone
- services, err := b.Services(state, false, opt)
- if err != nil {
- return nil, nil, err
- }
- // ... and reset
- state.Req.Question[0].Name = old
-
- for _, serv := range services {
- what, ip := serv.HostType()
- switch what {
- case dns.TypeCNAME:
- return nil, nil, fmt.Errorf("NS record must be an IP address: %s", serv.Host)
-
- case dns.TypeA, dns.TypeAAAA:
- serv.Host = msg.Domain(serv.Key)
- records = append(records, serv.NewNS(state.QName()))
- extra = append(extra, newAddress(serv, serv.Host, ip, what))
- }
- }
- return records, extra, nil
-}
-
-// SOA returns a SOA record from the backend.
-func SOA(b ServiceBackend, zone string, state request.Request, opt Options) ([]dns.RR, error) {
- header := dns.RR_Header{Name: zone, Rrtype: dns.TypeSOA, Ttl: 300, Class: dns.ClassINET}
-
- Mbox := hostmaster + "."
- Ns := "ns.dns."
- if zone[0] != '.' {
- Mbox += zone
- Ns += zone
- }
-
- soa := &dns.SOA{Hdr: header,
- Mbox: Mbox,
- Ns: Ns,
- Serial: uint32(time.Now().Unix()),
- Refresh: 7200,
- Retry: 1800,
- Expire: 86400,
- Minttl: minTTL,
- }
- return []dns.RR{soa}, nil
-}
-
-// BackendError writes an error response to the client.
-func BackendError(b ServiceBackend, zone string, rcode int, state request.Request, err error, opt Options) (int, error) {
- m := new(dns.Msg)
- m.SetRcode(state.Req, rcode)
- m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
- m.Ns, _ = SOA(b, zone, state, opt)
-
- state.SizeAndDo(m)
- state.W.WriteMsg(m)
- // Return success as the rcode to signal we have written to the client.
- return dns.RcodeSuccess, err
-}
-
-func newAddress(s msg.Service, name string, ip net.IP, what uint16) dns.RR {
-
- hdr := dns.RR_Header{Name: name, Rrtype: what, Class: dns.ClassINET, Ttl: s.TTL}
-
- if what == dns.TypeA {
- return &dns.A{Hdr: hdr, A: ip}
- }
- // Should always be dns.TypeAAAA
- return &dns.AAAA{Hdr: hdr, AAAA: ip}
-}
-
-const (
- minTTL = 60
- hostmaster = "hostmaster"
-)
diff --git a/middleware/bind/README.md b/middleware/bind/README.md
deleted file mode 100644
index 57b3c1e18..000000000
--- a/middleware/bind/README.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# bind
-
-*bind* overrides the host to which the server should bind.
-
-Normally, the listener binds to the wildcard host. However, you may force the listener to bind to
-another IP instead. This directive accepts only an address, not a port.
-
-## Syntax
-
-~~~ txt
-bind ADDRESS
-~~~
-
-**ADDRESS** is the IP address to bind to.
-
-## Examples
-
-To make your socket accessible only to that machine, bind to IP 127.0.0.1 (localhost):
-
-~~~ txt
-bind 127.0.0.1
-~~~
diff --git a/middleware/bind/bind.go b/middleware/bind/bind.go
deleted file mode 100644
index bd3c32b51..000000000
--- a/middleware/bind/bind.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// Package bind allows binding to a specific interface instead of bind to all of them.
-package bind
-
-import "github.com/mholt/caddy"
-
-func init() {
- caddy.RegisterPlugin("bind", caddy.Plugin{
- ServerType: "dns",
- Action: setupBind,
- })
-}
diff --git a/middleware/bind/bind_test.go b/middleware/bind/bind_test.go
deleted file mode 100644
index 11556f0bd..000000000
--- a/middleware/bind/bind_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package bind
-
-import (
- "testing"
-
- "github.com/coredns/coredns/core/dnsserver"
-
- "github.com/mholt/caddy"
-)
-
-func TestSetupBind(t *testing.T) {
- c := caddy.NewTestController("dns", `bind 1.2.3.4`)
- err := setupBind(c)
- if err != nil {
- t.Fatalf("Expected no errors, but got: %v", err)
- }
-
- cfg := dnsserver.GetConfig(c)
- if got, want := cfg.ListenHost, "1.2.3.4"; got != want {
- t.Errorf("Expected the config's ListenHost to be %s, was %s", want, got)
- }
-}
-
-func TestBindAddress(t *testing.T) {
- c := caddy.NewTestController("dns", `bind 1.2.3.bla`)
- err := setupBind(c)
- if err == nil {
- t.Fatalf("Expected errors, but got none")
- }
-}
diff --git a/middleware/bind/setup.go b/middleware/bind/setup.go
deleted file mode 100644
index 1e7178cc3..000000000
--- a/middleware/bind/setup.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package bind
-
-import (
- "fmt"
- "net"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func setupBind(c *caddy.Controller) error {
- config := dnsserver.GetConfig(c)
- for c.Next() {
- if !c.Args(&config.ListenHost) {
- return middleware.Error("bind", c.ArgErr())
- }
- }
- if net.ParseIP(config.ListenHost) == nil {
- return middleware.Error("bind", fmt.Errorf("not a valid IP address: %s", config.ListenHost))
- }
- return nil
-}
diff --git a/middleware/cache/README.md b/middleware/cache/README.md
deleted file mode 100644
index 6477fe891..000000000
--- a/middleware/cache/README.md
+++ /dev/null
@@ -1,68 +0,0 @@
-# cache
-
-*cache* enables a frontend cache. It will cache all records except zone transfers and metadata records.
-
-## Syntax
-
-~~~ txt
-cache [TTL] [ZONES...]
-~~~
-
-* **TTL** max TTL in seconds. If not specified, the maximum TTL will be used which is 3600 for
- noerror responses and 1800 for denial of existence ones.
- Setting a TTL of 300 *cache 300* would cache the record up to 300 seconds.
-* **ZONES** zones it should cache for. If empty, the zones from the configuration block are used.
-
-Each element in the cache is cached according to its TTL (with **TTL** as the max).
-For the negative cache, the SOA's MinTTL value is used. A cache can contain up to 10,000 items by
-default. A TTL of zero is not allowed.
-
-If you want more control:
-
-~~~ txt
-cache [TTL] [ZONES...] {
- success CAPACITY [TTL]
- denial CAPACITY [TTL]
- prefetch AMOUNT [[DURATION] [PERCENTAGE%]]
-}
-~~~
-
-* **TTL** and **ZONES** as above.
-* `success`, override the settings for caching successful responses, **CAPACITY** indicates the maximum
- number of packets we cache before we start evicting (*randomly*). **TTL** overrides the cache maximum TTL.
-* `denial`, override the settings for caching denial of existence responses, **CAPACITY** indicates the maximum
- number of packets we cache before we start evicting (LRU). **TTL** overrides the cache maximum TTL.
- There is a third category (`error`) but those responses are never cached.
-* `prefetch`, will prefetch popular items when they are about to be expunged from the cache.
- Popular means **AMOUNT** queries have been seen no gaps of **DURATION** or more between them.
- **DURATION** defaults to 1m. Prefetching will happen when the TTL drops below **PERCENTAGE**,
- which defaults to `10%`. Values should be in the range `[10%, 90%]`. Note the percent sign is
- mandatory. **PERCENTAGE** is treated as an `int`.
-
-The minimum TTL allowed on resource records is 5 seconds.
-
-## Metrics
-
-If monitoring is enabled (via the *prometheus* directive) then the following metrics are exported:
-
-* coredns_cache_size{type} - Total elements in the cache by cache type.
-* coredns_cache_capacity{type} - Total capacity of the cache by cache type.
-* coredns_cache_hits_total{type} - Counter of cache hits by cache type.
-* coredns_cache_misses_total - Counter of cache misses.
-
-Cache types are either "denial" or "success".
-
-## Examples
-
-Enable caching for all zones, but cap everything to a TTL of 10 seconds:
-
-~~~
-cache 10
-~~~
-
-Proxy to Google Public DNS and only cache responses for example.org (or below).
-
-~~~
-proxy . 8.8.8.8:53
-cache example.org
-~~~
diff --git a/middleware/cache/cache.go b/middleware/cache/cache.go
deleted file mode 100644
index 434efa296..000000000
--- a/middleware/cache/cache.go
+++ /dev/null
@@ -1,167 +0,0 @@
-// Package cache implements a cache.
-package cache
-
-import (
- "encoding/binary"
- "hash/fnv"
- "log"
- "time"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/cache"
- "github.com/coredns/coredns/middleware/pkg/response"
-
- "github.com/miekg/dns"
-)
-
-// Cache is middleware that looks up responses in a cache and caches replies.
-// It has a success and a denial of existence cache.
-type Cache struct {
- Next middleware.Handler
- Zones []string
-
- ncache *cache.Cache
- ncap int
- nttl time.Duration
-
- pcache *cache.Cache
- pcap int
- pttl time.Duration
-
- // Prefetch.
- prefetch int
- duration time.Duration
- percentage int
-}
-
-// Return key under which we store the item, -1 will be returned if we don't store the
-// message.
-// Currently we do not cache Truncated, errors zone transfers or dynamic update messages.
-func key(m *dns.Msg, t response.Type, do bool) int {
- // We don't store truncated responses.
- if m.Truncated {
- return -1
- }
- // Nor errors or Meta or Update
- if t == response.OtherError || t == response.Meta || t == response.Update {
- return -1
- }
-
- return int(hash(m.Question[0].Name, m.Question[0].Qtype, do))
-}
-
-var one = []byte("1")
-var zero = []byte("0")
-
-func hash(qname string, qtype uint16, do bool) uint32 {
- h := fnv.New32()
-
- if do {
- h.Write(one)
- } else {
- h.Write(zero)
- }
-
- b := make([]byte, 2)
- binary.BigEndian.PutUint16(b, qtype)
- h.Write(b)
-
- for i := range qname {
- c := qname[i]
- if c >= 'A' && c <= 'Z' {
- c += 'a' - 'A'
- }
- h.Write([]byte{c})
- }
-
- return h.Sum32()
-}
-
-// ResponseWriter is a response writer that caches the reply message.
-type ResponseWriter struct {
- dns.ResponseWriter
- *Cache
-
- prefetch bool // When true write nothing back to the client.
-}
-
-// WriteMsg implements the dns.ResponseWriter interface.
-func (w *ResponseWriter) WriteMsg(res *dns.Msg) error {
- do := false
- mt, opt := response.Typify(res, time.Now().UTC())
- if opt != nil {
- do = opt.Do()
- }
-
- // key returns empty string for anything we don't want to cache.
- key := key(res, mt, do)
-
- duration := w.pttl
- if mt == response.NameError || mt == response.NoData {
- duration = w.nttl
- }
-
- msgTTL := minMsgTTL(res, mt)
- if msgTTL < duration {
- duration = msgTTL
- }
-
- if key != -1 {
- w.set(res, key, mt, duration)
-
- cacheSize.WithLabelValues(Success).Set(float64(w.pcache.Len()))
- cacheSize.WithLabelValues(Denial).Set(float64(w.ncache.Len()))
- }
-
- if w.prefetch {
- return nil
- }
-
- return w.ResponseWriter.WriteMsg(res)
-}
-
-func (w *ResponseWriter) set(m *dns.Msg, key int, mt response.Type, duration time.Duration) {
- if key == -1 {
- log.Printf("[ERROR] Caching called with empty cache key")
- return
- }
-
- switch mt {
- case response.NoError, response.Delegation:
- i := newItem(m, duration)
- w.pcache.Add(uint32(key), i)
-
- case response.NameError, response.NoData:
- i := newItem(m, duration)
- w.ncache.Add(uint32(key), i)
-
- case response.OtherError:
- // don't cache these
- default:
- log.Printf("[WARNING] Caching called with unknown classification: %d", mt)
- }
-}
-
-// Write implements the dns.ResponseWriter interface.
-func (w *ResponseWriter) Write(buf []byte) (int, error) {
- log.Printf("[WARNING] Caching called with Write: not caching reply")
- if w.prefetch {
- return 0, nil
- }
- n, err := w.ResponseWriter.Write(buf)
- return n, err
-}
-
-const (
- maxTTL = 1 * time.Hour
- maxNTTL = 30 * time.Minute
-
- minTTL = 5 // seconds
-
- defaultCap = 10000 // default capacity of the cache.
-
- // Success is the class for caching positive caching.
- Success = "success"
- // Denial is the class defined for negative caching.
- Denial = "denial"
-)
diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go
deleted file mode 100644
index f364e69f1..000000000
--- a/middleware/cache/cache_test.go
+++ /dev/null
@@ -1,251 +0,0 @@
-package cache
-
-import (
- "io/ioutil"
- "log"
- "testing"
- "time"
-
- "golang.org/x/net/context"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/cache"
- "github.com/coredns/coredns/middleware/pkg/response"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
-)
-
-type cacheTestCase struct {
- test.Case
- in test.Case
- AuthenticatedData bool
- Authoritative bool
- RecursionAvailable bool
- Truncated bool
- shouldCache bool
-}
-
-var cacheTestCases = []cacheTestCase{
- {
- RecursionAvailable: true, AuthenticatedData: true, Authoritative: true,
- Case: test.Case{
- Qname: "miek.nl.", Qtype: dns.TypeMX,
- Answer: []dns.RR{
- test.MX("miek.nl. 3600 IN MX 1 aspmx.l.google.com."),
- test.MX("miek.nl. 3600 IN MX 10 aspmx2.googlemail.com."),
- },
- },
- in: test.Case{
- Qname: "miek.nl.", Qtype: dns.TypeMX,
- Answer: []dns.RR{
- test.MX("miek.nl. 3601 IN MX 1 aspmx.l.google.com."),
- test.MX("miek.nl. 3601 IN MX 10 aspmx2.googlemail.com."),
- },
- },
- shouldCache: true,
- },
- {
- RecursionAvailable: true, AuthenticatedData: true, Authoritative: true,
- Case: test.Case{
- Qname: "mIEK.nL.", Qtype: dns.TypeMX,
- Answer: []dns.RR{
- test.MX("mIEK.nL. 3600 IN MX 1 aspmx.l.google.com."),
- test.MX("mIEK.nL. 3600 IN MX 10 aspmx2.googlemail.com."),
- },
- },
- in: test.Case{
- Qname: "mIEK.nL.", Qtype: dns.TypeMX,
- Answer: []dns.RR{
- test.MX("mIEK.nL. 3601 IN MX 1 aspmx.l.google.com."),
- test.MX("mIEK.nL. 3601 IN MX 10 aspmx2.googlemail.com."),
- },
- },
- shouldCache: true,
- },
- {
- Truncated: true,
- Case: test.Case{
- Qname: "miek.nl.", Qtype: dns.TypeMX,
- Answer: []dns.RR{test.MX("miek.nl. 1800 IN MX 1 aspmx.l.google.com.")},
- },
- in: test.Case{},
- shouldCache: false,
- },
- {
- RecursionAvailable: true, Authoritative: true,
- Case: test.Case{
- Rcode: dns.RcodeNameError,
- Qname: "example.org.", Qtype: dns.TypeA,
- Ns: []dns.RR{
- test.SOA("example.org. 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2016082540 7200 3600 1209600 3600"),
- },
- },
- in: test.Case{
- Rcode: dns.RcodeNameError,
- Qname: "example.org.", Qtype: dns.TypeA,
- Ns: []dns.RR{
- test.SOA("example.org. 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2016082540 7200 3600 1209600 3600"),
- },
- },
- shouldCache: true,
- },
- {
- RecursionAvailable: true, Authoritative: true,
- Case: test.Case{
- Qname: "miek.nl.", Qtype: dns.TypeMX,
- Do: true,
- Answer: []dns.RR{
- test.MX("miek.nl. 3600 IN MX 1 aspmx.l.google.com."),
- test.MX("miek.nl. 3600 IN MX 10 aspmx2.googlemail.com."),
- test.RRSIG("miek.nl. 3600 IN RRSIG MX 8 2 1800 20160521031301 20160421031301 12051 miek.nl. lAaEzB5teQLLKyDenatmyhca7blLRg9DoGNrhe3NReBZN5C5/pMQk8Jc u25hv2fW23/SLm5IC2zaDpp2Fzgm6Jf7e90/yLcwQPuE7JjS55WMF+HE LEh7Z6AEb+Iq4BWmNhUz6gPxD4d9eRMs7EAzk13o1NYi5/JhfL6IlaYy qkc="),
- },
- },
- in: test.Case{
- Qname: "miek.nl.", Qtype: dns.TypeMX,
- Do: true,
- Answer: []dns.RR{
- test.MX("miek.nl. 3600 IN MX 1 aspmx.l.google.com."),
- test.MX("miek.nl. 3600 IN MX 10 aspmx2.googlemail.com."),
- test.RRSIG("miek.nl. 1800 IN RRSIG MX 8 2 1800 20160521031301 20160421031301 12051 miek.nl. lAaEzB5teQLLKyDenatmyhca7blLRg9DoGNrhe3NReBZN5C5/pMQk8Jc u25hv2fW23/SLm5IC2zaDpp2Fzgm6Jf7e90/yLcwQPuE7JjS55WMF+HE LEh7Z6AEb+Iq4BWmNhUz6gPxD4d9eRMs7EAzk13o1NYi5/JhfL6IlaYy qkc="),
- },
- },
- shouldCache: false,
- },
- {
- RecursionAvailable: true, Authoritative: true,
- Case: test.Case{
- Qname: "example.org.", Qtype: dns.TypeMX,
- Do: true,
- Answer: []dns.RR{
- test.MX("example.org. 3600 IN MX 1 aspmx.l.google.com."),
- test.MX("example.org. 3600 IN MX 10 aspmx2.googlemail.com."),
- test.RRSIG("example.org. 3600 IN RRSIG MX 8 2 1800 20170521031301 20170421031301 12051 miek.nl. lAaEzB5teQLLKyDenatmyhca7blLRg9DoGNrhe3NReBZN5C5/pMQk8Jc u25hv2fW23/SLm5IC2zaDpp2Fzgm6Jf7e90/yLcwQPuE7JjS55WMF+HE LEh7Z6AEb+Iq4BWmNhUz6gPxD4d9eRMs7EAzk13o1NYi5/JhfL6IlaYy qkc="),
- },
- },
- in: test.Case{
- Qname: "example.org.", Qtype: dns.TypeMX,
- Do: true,
- Answer: []dns.RR{
- test.MX("example.org. 3600 IN MX 1 aspmx.l.google.com."),
- test.MX("example.org. 3600 IN MX 10 aspmx2.googlemail.com."),
- test.RRSIG("example.org. 1800 IN RRSIG MX 8 2 1800 20170521031301 20170421031301 12051 miek.nl. lAaEzB5teQLLKyDenatmyhca7blLRg9DoGNrhe3NReBZN5C5/pMQk8Jc u25hv2fW23/SLm5IC2zaDpp2Fzgm6Jf7e90/yLcwQPuE7JjS55WMF+HE LEh7Z6AEb+Iq4BWmNhUz6gPxD4d9eRMs7EAzk13o1NYi5/JhfL6IlaYy qkc="),
- },
- },
- shouldCache: true,
- },
-}
-
-func cacheMsg(m *dns.Msg, tc cacheTestCase) *dns.Msg {
- m.RecursionAvailable = tc.RecursionAvailable
- m.AuthenticatedData = tc.AuthenticatedData
- m.Authoritative = tc.Authoritative
- m.Rcode = tc.Rcode
- m.Truncated = tc.Truncated
- m.Answer = tc.in.Answer
- m.Ns = tc.in.Ns
- // m.Extra = tc.in.Extra don't copy Extra, because we don't care and fake EDNS0 DO with tc.Do.
- return m
-}
-
-func newTestCache(ttl time.Duration) (*Cache, *ResponseWriter) {
- c := &Cache{Zones: []string{"."}, pcap: defaultCap, ncap: defaultCap, pttl: ttl, nttl: ttl}
- c.pcache = cache.New(c.pcap)
- c.ncache = cache.New(c.ncap)
-
- crr := &ResponseWriter{ResponseWriter: nil, Cache: c}
- return c, crr
-}
-
-func TestCache(t *testing.T) {
- now, _ := time.Parse(time.UnixDate, "Fri Apr 21 10:51:21 BST 2017")
- utc := now.UTC()
-
- c, crr := newTestCache(maxTTL)
-
- log.SetOutput(ioutil.Discard)
-
- for _, tc := range cacheTestCases {
- m := tc.in.Msg()
- m = cacheMsg(m, tc)
- do := tc.in.Do
-
- mt, _ := response.Typify(m, utc)
- k := key(m, mt, do)
-
- crr.set(m, k, mt, c.pttl)
-
- name := middleware.Name(m.Question[0].Name).Normalize()
- qtype := m.Question[0].Qtype
-
- i, _ := c.get(time.Now().UTC(), name, qtype, do)
- ok := i != nil
-
- if ok != tc.shouldCache {
- t.Errorf("cached message that should not have been cached: %s", name)
- continue
- }
-
- if ok {
- resp := i.toMsg(m)
-
- if !test.Header(t, tc.Case, resp) {
- t.Logf("%v\n", resp)
- continue
- }
-
- if !test.Section(t, tc.Case, test.Answer, resp.Answer) {
- t.Logf("%v\n", resp)
- }
- if !test.Section(t, tc.Case, test.Ns, resp.Ns) {
- t.Logf("%v\n", resp)
-
- }
- if !test.Section(t, tc.Case, test.Extra, resp.Extra) {
- t.Logf("%v\n", resp)
- }
- }
- }
-}
-
-func BenchmarkCacheResponse(b *testing.B) {
- c := &Cache{Zones: []string{"."}, pcap: defaultCap, ncap: defaultCap, pttl: maxTTL, nttl: maxTTL}
- c.pcache = cache.New(c.pcap)
- c.ncache = cache.New(c.ncap)
- c.prefetch = 1
- c.duration = 1 * time.Second
- c.Next = BackendHandler()
-
- ctx := context.TODO()
-
- reqs := make([]*dns.Msg, 5)
- for i, q := range []string{"example1", "example2", "a", "b", "ddd"} {
- reqs[i] = new(dns.Msg)
- reqs[i].SetQuestion(q+".example.org.", dns.TypeA)
- }
-
- b.RunParallel(func(pb *testing.PB) {
- i := 0
- for pb.Next() {
- req := reqs[i]
- c.ServeDNS(ctx, &test.ResponseWriter{}, req)
- i++
- i = i % 5
- }
- })
-}
-
-func BackendHandler() middleware.Handler {
- return middleware.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- m := new(dns.Msg)
- m.SetReply(r)
- m.Response = true
- m.RecursionAvailable = true
-
- owner := m.Question[0].Name
- m.Answer = []dns.RR{test.A(owner + " 303 IN A 127.0.0.53")}
-
- w.WriteMsg(m)
- return dns.RcodeSuccess, nil
- })
-}
diff --git a/middleware/cache/freq/freq.go b/middleware/cache/freq/freq.go
deleted file mode 100644
index f545f222e..000000000
--- a/middleware/cache/freq/freq.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Package freq keeps track of last X seen events. The events themselves are not stored
-// here. So the Freq type should be added next to the thing it is tracking.
-package freq
-
-import (
- "sync"
- "time"
-)
-
-// Freq tracks the frequencies of things.
-type Freq struct {
- // Last time we saw a query for this element.
- last time.Time
- // Number of this in the last time slice.
- hits int
-
- sync.RWMutex
-}
-
-// New returns a new initialized Freq.
-func New(t time.Time) *Freq {
- return &Freq{last: t, hits: 0}
-}
-
-// Update updates the number of hits. Last time seen will be set to now.
-// If the last time we've seen this entity is within now - d, we increment hits, otherwise
-// we reset hits to 1. It returns the number of hits.
-func (f *Freq) Update(d time.Duration, now time.Time) int {
- earliest := now.Add(-1 * d)
- f.Lock()
- defer f.Unlock()
- if f.last.Before(earliest) {
- f.last = now
- f.hits = 1
- return f.hits
- }
- f.last = now
- f.hits++
- return f.hits
-}
-
-// Hits returns the number of hits that we have seen, according to the updates we have done to f.
-func (f *Freq) Hits() int {
- f.RLock()
- defer f.RUnlock()
- return f.hits
-}
-
-// Reset resets f to time t and hits to hits.
-func (f *Freq) Reset(t time.Time, hits int) {
- f.Lock()
- defer f.Unlock()
- f.last = t
- f.hits = hits
-}
diff --git a/middleware/cache/freq/freq_test.go b/middleware/cache/freq/freq_test.go
deleted file mode 100644
index 740194c86..000000000
--- a/middleware/cache/freq/freq_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package freq
-
-import (
- "testing"
- "time"
-)
-
-func TestFreqUpdate(t *testing.T) {
- now := time.Now().UTC()
- f := New(now)
- window := 1 * time.Minute
-
- f.Update(window, time.Now().UTC())
- f.Update(window, time.Now().UTC())
- f.Update(window, time.Now().UTC())
- hitsCheck(t, f, 3)
-
- f.Reset(now, 0)
- history := time.Now().UTC().Add(-3 * time.Minute)
- f.Update(window, history)
- hitsCheck(t, f, 1)
-}
-
-func TestReset(t *testing.T) {
- f := New(time.Now().UTC())
- f.Update(1*time.Minute, time.Now().UTC())
- hitsCheck(t, f, 1)
- f.Reset(time.Now().UTC(), 0)
- hitsCheck(t, f, 0)
-}
-
-func hitsCheck(t *testing.T, f *Freq, expected int) {
- if x := f.Hits(); x != expected {
- t.Fatalf("Expected hits to be %d, got %d", expected, x)
- }
-}
diff --git a/middleware/cache/handler.go b/middleware/cache/handler.go
deleted file mode 100644
index 90c63d93a..000000000
--- a/middleware/cache/handler.go
+++ /dev/null
@@ -1,119 +0,0 @@
-package cache
-
-import (
- "time"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "github.com/prometheus/client_golang/prometheus"
- "golang.org/x/net/context"
-)
-
-// ServeDNS implements the middleware.Handler interface.
-func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- state := request.Request{W: w, Req: r}
-
- qname := state.Name()
- qtype := state.QType()
- zone := middleware.Zones(c.Zones).Matches(qname)
- if zone == "" {
- return c.Next.ServeDNS(ctx, w, r)
- }
-
- do := state.Do() // TODO(): might need more from OPT record? Like the actual bufsize?
-
- now := time.Now().UTC()
-
- i, ttl := c.get(now, qname, qtype, do)
- if i != nil && ttl > 0 {
- resp := i.toMsg(r)
-
- state.SizeAndDo(resp)
- resp, _ = state.Scrub(resp)
- w.WriteMsg(resp)
-
- i.Freq.Update(c.duration, now)
-
- pct := 100
- if i.origTTL != 0 { // you'll never know
- pct = int(float64(ttl) / float64(i.origTTL) * 100)
- }
-
- if c.prefetch > 0 && i.Freq.Hits() > c.prefetch && pct < c.percentage {
- // When prefetching we loose the item i, and with it the frequency
- // that we've gathered sofar. See we copy the frequencies info back
- // into the new item that was stored in the cache.
- prr := &ResponseWriter{ResponseWriter: w, Cache: c, prefetch: true}
- middleware.NextOrFailure(c.Name(), c.Next, ctx, prr, r)
-
- if i1, _ := c.get(now, qname, qtype, do); i1 != nil {
- i1.Freq.Reset(now, i.Freq.Hits())
- }
- }
-
- return dns.RcodeSuccess, nil
- }
-
- crr := &ResponseWriter{ResponseWriter: w, Cache: c}
- return middleware.NextOrFailure(c.Name(), c.Next, ctx, crr, r)
-}
-
-// Name implements the Handler interface.
-func (c *Cache) Name() string { return "cache" }
-
-func (c *Cache) get(now time.Time, qname string, qtype uint16, do bool) (*item, int) {
- k := hash(qname, qtype, do)
-
- if i, ok := c.ncache.Get(k); ok {
- cacheHits.WithLabelValues(Denial).Inc()
- return i.(*item), i.(*item).ttl(now)
- }
-
- if i, ok := c.pcache.Get(k); ok {
- cacheHits.WithLabelValues(Success).Inc()
- return i.(*item), i.(*item).ttl(now)
- }
- cacheMisses.Inc()
- return nil, 0
-}
-
-var (
- cacheSize = prometheus.NewGaugeVec(prometheus.GaugeOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "size",
- Help: "The number of elements in the cache.",
- }, []string{"type"})
-
- cacheCapacity = prometheus.NewGaugeVec(prometheus.GaugeOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "capacity",
- Help: "The cache's capacity.",
- }, []string{"type"})
-
- cacheHits = prometheus.NewCounterVec(prometheus.CounterOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "hits_total",
- Help: "The count of cache hits.",
- }, []string{"type"})
-
- cacheMisses = prometheus.NewCounter(prometheus.CounterOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "misses_total",
- Help: "The count of cache misses.",
- })
-)
-
-const subsystem = "cache"
-
-func init() {
- prometheus.MustRegister(cacheSize)
- prometheus.MustRegister(cacheCapacity)
- prometheus.MustRegister(cacheHits)
- prometheus.MustRegister(cacheMisses)
-}
diff --git a/middleware/cache/item.go b/middleware/cache/item.go
deleted file mode 100644
index 02571ac5c..000000000
--- a/middleware/cache/item.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package cache
-
-import (
- "time"
-
- "github.com/coredns/coredns/middleware/cache/freq"
- "github.com/coredns/coredns/middleware/pkg/response"
- "github.com/miekg/dns"
-)
-
-type item struct {
- Rcode int
- Authoritative bool
- AuthenticatedData bool
- RecursionAvailable bool
- Answer []dns.RR
- Ns []dns.RR
- Extra []dns.RR
-
- origTTL uint32
- stored time.Time
-
- *freq.Freq
-}
-
-func newItem(m *dns.Msg, d time.Duration) *item {
- i := new(item)
- i.Rcode = m.Rcode
- i.Authoritative = m.Authoritative
- i.AuthenticatedData = m.AuthenticatedData
- i.RecursionAvailable = m.RecursionAvailable
- i.Answer = m.Answer
- i.Ns = m.Ns
- i.Extra = make([]dns.RR, len(m.Extra))
- // Don't copy OPT record as these are hop-by-hop.
- j := 0
- for _, e := range m.Extra {
- if e.Header().Rrtype == dns.TypeOPT {
- continue
- }
- i.Extra[j] = e
- j++
- }
- i.Extra = i.Extra[:j]
-
- i.origTTL = uint32(d.Seconds())
- i.stored = time.Now().UTC()
-
- i.Freq = new(freq.Freq)
-
- return i
-}
-
-// toMsg turns i into a message, it tailors the reply to m.
-// The Authoritative bit is always set to 0, because the answer is from the cache.
-func (i *item) toMsg(m *dns.Msg) *dns.Msg {
- m1 := new(dns.Msg)
- m1.SetReply(m)
-
- m1.Authoritative = false
- m1.AuthenticatedData = i.AuthenticatedData
- m1.RecursionAvailable = i.RecursionAvailable
- m1.Rcode = i.Rcode
- m1.Compress = true
-
- m1.Answer = make([]dns.RR, len(i.Answer))
- m1.Ns = make([]dns.RR, len(i.Ns))
- m1.Extra = make([]dns.RR, len(i.Extra))
-
- ttl := uint32(i.ttl(time.Now()))
- if ttl < minTTL {
- ttl = minTTL
- }
-
- for j, r := range i.Answer {
- m1.Answer[j] = dns.Copy(r)
- m1.Answer[j].Header().Ttl = ttl
- }
- for j, r := range i.Ns {
- m1.Ns[j] = dns.Copy(r)
- m1.Ns[j].Header().Ttl = ttl
- }
- for j, r := range i.Extra {
- m1.Extra[j] = dns.Copy(r)
- if m1.Extra[j].Header().Rrtype != dns.TypeOPT {
- m1.Extra[j].Header().Ttl = ttl
- }
- }
- return m1
-}
-
-func (i *item) ttl(now time.Time) int {
- ttl := int(i.origTTL) - int(now.UTC().Sub(i.stored).Seconds())
- return ttl
-}
-
-func minMsgTTL(m *dns.Msg, mt response.Type) time.Duration {
- if mt != response.NoError && mt != response.NameError && mt != response.NoData {
- return 0
- }
-
- minTTL := maxTTL
- for _, r := range append(m.Answer, m.Ns...) {
- switch mt {
- case response.NameError, response.NoData:
- if r.Header().Rrtype == dns.TypeSOA {
- return time.Duration(r.(*dns.SOA).Minttl) * time.Second
- }
- case response.NoError, response.Delegation:
- if r.Header().Ttl < uint32(minTTL.Seconds()) {
- minTTL = time.Duration(r.Header().Ttl) * time.Second
- }
- }
- }
- return minTTL
-}
diff --git a/middleware/cache/prefech_test.go b/middleware/cache/prefech_test.go
deleted file mode 100644
index 69ad5f92a..000000000
--- a/middleware/cache/prefech_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package cache
-
-import (
- "fmt"
- "testing"
- "time"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/cache"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
-
- "github.com/coredns/coredns/middleware/test"
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-var p = false
-
-func TestPrefetch(t *testing.T) {
- c := &Cache{Zones: []string{"."}, pcap: defaultCap, ncap: defaultCap, pttl: maxTTL, nttl: maxTTL}
- c.pcache = cache.New(c.pcap)
- c.ncache = cache.New(c.ncap)
- c.prefetch = 1
- c.duration = 1 * time.Second
- c.Next = PrefetchHandler(t, dns.RcodeSuccess, nil)
-
- ctx := context.TODO()
-
- req := new(dns.Msg)
- req.SetQuestion("lowttl.example.org.", dns.TypeA)
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
-
- c.ServeDNS(ctx, rec, req)
- p = true // prefetch should be true for the 2nd fetch
- c.ServeDNS(ctx, rec, req)
-}
-
-func PrefetchHandler(t *testing.T, rcode int, err error) middleware.Handler {
- return middleware.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- m := new(dns.Msg)
- m.SetQuestion("lowttl.example.org.", dns.TypeA)
- m.Response = true
- m.RecursionAvailable = true
- m.Answer = append(m.Answer, test.A("lowttl.example.org. 80 IN A 127.0.0.53"))
- if p != w.(*ResponseWriter).prefetch {
- err = fmt.Errorf("cache prefetch not equal to p: got %t, want %t", p, w.(*ResponseWriter).prefetch)
- t.Fatal(err)
- }
-
- w.WriteMsg(m)
- return rcode, err
- })
-}
diff --git a/middleware/cache/setup.go b/middleware/cache/setup.go
deleted file mode 100644
index 65cfb70d1..000000000
--- a/middleware/cache/setup.go
+++ /dev/null
@@ -1,170 +0,0 @@
-package cache
-
-import (
- "fmt"
- "strconv"
- "time"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/cache"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("cache", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- ca, err := cacheParse(c)
- if err != nil {
- return middleware.Error("cache", err)
- }
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- ca.Next = next
- return ca
- })
-
- // Export the capacity for the metrics. This only happens once, because this is a re-load change only.
- cacheCapacity.WithLabelValues(Success).Set(float64(ca.pcap))
- cacheCapacity.WithLabelValues(Denial).Set(float64(ca.ncap))
-
- return nil
-}
-
-func cacheParse(c *caddy.Controller) (*Cache, error) {
-
- ca := &Cache{pcap: defaultCap, ncap: defaultCap, pttl: maxTTL, nttl: maxNTTL, prefetch: 0, duration: 1 * time.Minute}
-
- for c.Next() {
- // cache [ttl] [zones..]
- origins := make([]string, len(c.ServerBlockKeys))
- copy(origins, c.ServerBlockKeys)
- args := c.RemainingArgs()
-
- if len(args) > 0 {
- // first args may be just a number, then it is the ttl, if not it is a zone
- ttl, err := strconv.Atoi(args[0])
- if err == nil {
- // Reserve 0 (and smaller for future things)
- if ttl <= 0 {
- return nil, fmt.Errorf("cache TTL can not be zero or negative: %d", ttl)
- }
- ca.pttl = time.Duration(ttl) * time.Second
- ca.nttl = time.Duration(ttl) * time.Second
- args = args[1:]
- }
- if len(args) > 0 {
- copy(origins, args)
- }
- }
-
- // Refinements? In an extra block.
- for c.NextBlock() {
- switch c.Val() {
- // first number is cap, second is an new ttl
- case Success:
- args := c.RemainingArgs()
- if len(args) == 0 {
- return nil, c.ArgErr()
- }
- pcap, err := strconv.Atoi(args[0])
- if err != nil {
- return nil, err
- }
- ca.pcap = pcap
- if len(args) > 1 {
- pttl, err := strconv.Atoi(args[1])
- if err != nil {
- return nil, err
- }
- // Reserve 0 (and smaller for future things)
- if pttl <= 0 {
- return nil, fmt.Errorf("cache TTL can not be zero or negative: %d", pttl)
- }
- ca.pttl = time.Duration(pttl) * time.Second
- }
- case Denial:
- args := c.RemainingArgs()
- if len(args) == 0 {
- return nil, c.ArgErr()
- }
- ncap, err := strconv.Atoi(args[0])
- if err != nil {
- return nil, err
- }
- ca.ncap = ncap
- if len(args) > 1 {
- nttl, err := strconv.Atoi(args[1])
- if err != nil {
- return nil, err
- }
- // Reserve 0 (and smaller for future things)
- if nttl <= 0 {
- return nil, fmt.Errorf("cache TTL can not be zero or negative: %d", nttl)
- }
- ca.nttl = time.Duration(nttl) * time.Second
- }
- case "prefetch":
- args := c.RemainingArgs()
- if len(args) == 0 || len(args) > 3 {
- return nil, c.ArgErr()
- }
- amount, err := strconv.Atoi(args[0])
- if err != nil {
- return nil, err
- }
- if amount < 0 {
- return nil, fmt.Errorf("prefetch amount should be positive: %d", amount)
- }
- ca.prefetch = amount
-
- ca.duration = 1 * time.Minute
- ca.percentage = 10
- if len(args) > 1 {
- dur, err := time.ParseDuration(args[1])
- if err != nil {
- return nil, err
- }
- ca.duration = dur
- }
- if len(args) > 2 {
- pct := args[2]
- if x := pct[len(pct)-1]; x != '%' {
- return nil, fmt.Errorf("last character of percentage should be `%%`, but is: %q", x)
- }
- pct = pct[:len(pct)-1]
-
- num, err := strconv.Atoi(pct)
- if err != nil {
- return nil, err
- }
- if num < 10 || num > 90 {
- return nil, fmt.Errorf("percentage should fall in range [10, 90]: %d", num)
- }
- ca.percentage = num
- }
-
- default:
- return nil, c.ArgErr()
- }
- }
-
- for i := range origins {
- origins[i] = middleware.Host(origins[i]).Normalize()
- }
-
- ca.Zones = origins
-
- ca.pcache = cache.New(ca.pcap)
- ca.ncache = cache.New(ca.ncap)
-
- return ca, nil
- }
-
- return nil, nil
-}
diff --git a/middleware/cache/setup_test.go b/middleware/cache/setup_test.go
deleted file mode 100644
index afc2ecc13..000000000
--- a/middleware/cache/setup_test.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package cache
-
-import (
- "testing"
- "time"
-
- "github.com/mholt/caddy"
-)
-
-func TestSetup(t *testing.T) {
- tests := []struct {
- input string
- shouldErr bool
- expectedNcap int
- expectedPcap int
- expectedNttl time.Duration
- expectedPttl time.Duration
- expectedPrefetch int
- }{
- {`cache`, false, defaultCap, defaultCap, maxNTTL, maxTTL, 0},
- {`cache {}`, false, defaultCap, defaultCap, maxNTTL, maxTTL, 0},
- {`cache example.nl {
- success 10
- }`, false, defaultCap, 10, maxNTTL, maxTTL, 0},
- {`cache example.nl {
- success 10
- denial 10 15
- }`, false, 10, 10, 15 * time.Second, maxTTL, 0},
- {`cache 25 example.nl {
- success 10
- denial 10 15
- }`, false, 10, 10, 15 * time.Second, 25 * time.Second, 0},
- {`cache aaa example.nl`, false, defaultCap, defaultCap, maxNTTL, maxTTL, 0},
- {`cache {
- prefetch 10
- }`, false, defaultCap, defaultCap, maxNTTL, maxTTL, 10},
-
- // fails
- {`cache example.nl {
- success
- denial 10 15
- }`, true, defaultCap, defaultCap, maxTTL, maxTTL, 0},
- {`cache example.nl {
- success 15
- denial aaa
- }`, true, defaultCap, defaultCap, maxTTL, maxTTL, 0},
- {`cache example.nl {
- positive 15
- negative aaa
- }`, true, defaultCap, defaultCap, maxTTL, maxTTL, 0},
- {`cache 0 example.nl`, true, defaultCap, defaultCap, maxTTL, maxTTL, 0},
- {`cache -1 example.nl`, true, defaultCap, defaultCap, maxTTL, maxTTL, 0},
- {`cache 1 example.nl {
- positive 0
- }`, true, defaultCap, defaultCap, maxTTL, maxTTL, 0},
- {`cache 1 example.nl {
- positive 0
- prefetch -1
- }`, true, defaultCap, defaultCap, maxTTL, maxTTL, 0},
- {`cache 1 example.nl {
- prefetch 0 blurp
- }`, true, defaultCap, defaultCap, maxTTL, maxTTL, 0},
- }
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- ca, err := cacheParse(c)
- if test.shouldErr && err == nil {
- t.Errorf("Test %v: Expected error but found nil", i)
- continue
- } else if !test.shouldErr && err != nil {
- t.Errorf("Test %v: Expected no error but found error: %v", i, err)
- continue
- }
- if test.shouldErr && err != nil {
- continue
- }
-
- if ca.ncap != test.expectedNcap {
- t.Errorf("Test %v: Expected ncap %v but found: %v", i, test.expectedNcap, ca.ncap)
- }
- if ca.pcap != test.expectedPcap {
- t.Errorf("Test %v: Expected pcap %v but found: %v", i, test.expectedPcap, ca.pcap)
- }
- if ca.nttl != test.expectedNttl {
- t.Errorf("Test %v: Expected nttl %v but found: %v", i, test.expectedNttl, ca.nttl)
- }
- if ca.pttl != test.expectedPttl {
- t.Errorf("Test %v: Expected pttl %v but found: %v", i, test.expectedPttl, ca.pttl)
- }
- if ca.prefetch != test.expectedPrefetch {
- t.Errorf("Test %v: Expected prefetch %v but found: %v", i, test.expectedPrefetch, ca.prefetch)
- }
- }
-}
diff --git a/middleware/chaos/README.md b/middleware/chaos/README.md
deleted file mode 100644
index 264ab0601..000000000
--- a/middleware/chaos/README.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# chaos
-
-The *chaos* middleware allows CoreDNS to respond to TXT queries in the CH class.
-
-This is useful for retrieving version or author information from the server.
-
-## Syntax
-
-~~~
-chaos [VERSION] [AUTHORS...]
-~~~
-
-* **VERSION** is the version to return. Defaults to `CoreDNS-<version>`, if not set.
-* **AUTHORS** is what authors to return. No default.
-
-Note that you have to make sure that this middleware will get actual queries for the
-following zones: `version.bind`, `version.server`, `authors.bind`, `hostname.bind` and
-`id.server`.
-
-## Examples
-
-Specify all the zones in full.
-
-~~~ corefile
-version.bind version.server authors.bind hostname.bind id.server {
- chaos CoreDNS-001 info@coredns.io
-}
-~~~
-
-Or just default to `.`:
-
-~~~ corefile
-. {
- chaos CoreDNS-001 info@coredns.io
-}
-~~~
-
-And test with `dig`:
-
-~~~ txt
-% dig @localhost CH TXT version.bind
-...
-;; ANSWER SECTION:
-version.bind. 0 CH TXT "CoreDNS-001"
-...
-~~~
diff --git a/middleware/chaos/chaos.go b/middleware/chaos/chaos.go
deleted file mode 100644
index e320a1b7a..000000000
--- a/middleware/chaos/chaos.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// Package chaos implements a middleware that answer to 'CH version.bind TXT' type queries.
-package chaos
-
-import (
- "os"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// Chaos allows CoreDNS to reply to CH TXT queries and return author or
-// version information.
-type Chaos struct {
- Next middleware.Handler
- Version string
- Authors map[string]bool
-}
-
-// ServeDNS implements the middleware.Handler interface.
-func (c Chaos) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- state := request.Request{W: w, Req: r}
- if state.QClass() != dns.ClassCHAOS || state.QType() != dns.TypeTXT {
- return middleware.NextOrFailure(c.Name(), c.Next, ctx, w, r)
- }
-
- m := new(dns.Msg)
- m.SetReply(r)
-
- hdr := dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}
- switch state.Name() {
- default:
- return c.Next.ServeDNS(ctx, w, r)
- case "authors.bind.":
- for a := range c.Authors {
- m.Answer = append(m.Answer, &dns.TXT{Hdr: hdr, Txt: []string{trim(a)}})
- }
- case "version.bind.", "version.server.":
- m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{trim(c.Version)}}}
- case "hostname.bind.", "id.server.":
- hostname, err := os.Hostname()
- if err != nil {
- hostname = "localhost"
- }
- m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{trim(hostname)}}}
- }
- state.SizeAndDo(m)
- w.WriteMsg(m)
- return 0, nil
-}
-
-// Name implements the Handler interface.
-func (c Chaos) Name() string { return "chaos" }
-
-func trim(s string) string {
- if len(s) < 256 {
- return s
- }
- return s[:255]
-}
diff --git a/middleware/chaos/chaos_test.go b/middleware/chaos/chaos_test.go
deleted file mode 100644
index dd383370b..000000000
--- a/middleware/chaos/chaos_test.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package chaos
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func TestChaos(t *testing.T) {
- em := Chaos{
- Version: version,
- Authors: map[string]bool{"Miek Gieben": true},
- }
-
- tests := []struct {
- next middleware.Handler
- qname string
- qtype uint16
- expectedCode int
- expectedReply string
- expectedErr error
- }{
- {
- next: test.NextHandler(dns.RcodeSuccess, nil),
- qname: "version.bind",
- expectedCode: dns.RcodeSuccess,
- expectedReply: version,
- expectedErr: nil,
- },
- {
- next: test.NextHandler(dns.RcodeSuccess, nil),
- qname: "authors.bind",
- expectedCode: dns.RcodeSuccess,
- expectedReply: "Miek Gieben",
- expectedErr: nil,
- },
- {
- next: test.NextHandler(dns.RcodeSuccess, nil),
- qname: "authors.bind",
- qtype: dns.TypeSRV,
- expectedCode: dns.RcodeSuccess,
- expectedErr: nil,
- },
- }
-
- ctx := context.TODO()
-
- for i, tc := range tests {
- req := new(dns.Msg)
- if tc.qtype == 0 {
- tc.qtype = dns.TypeTXT
- }
- req.SetQuestion(dns.Fqdn(tc.qname), tc.qtype)
- req.Question[0].Qclass = dns.ClassCHAOS
- em.Next = tc.next
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- code, err := em.ServeDNS(ctx, rec, req)
-
- if err != tc.expectedErr {
- t.Errorf("Test %d: Expected error %v, but got %v", i, tc.expectedErr, err)
- }
- if code != int(tc.expectedCode) {
- t.Errorf("Test %d: Expected status code %d, but got %d", i, tc.expectedCode, code)
- }
- if tc.expectedReply != "" {
- answer := rec.Msg.Answer[0].(*dns.TXT).Txt[0]
- if answer != tc.expectedReply {
- t.Errorf("Test %d: Expected answer %s, but got %s", i, tc.expectedReply, answer)
- }
- }
- }
-}
-
-const version = "CoreDNS-001"
diff --git a/middleware/chaos/setup.go b/middleware/chaos/setup.go
deleted file mode 100644
index 17b4f90cc..000000000
--- a/middleware/chaos/setup.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package chaos
-
-import (
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("chaos", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-
-}
-
-func setup(c *caddy.Controller) error {
- version, authors, err := chaosParse(c)
- if err != nil {
- return middleware.Error("chaos", err)
- }
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- return Chaos{Next: next, Version: version, Authors: authors}
- })
-
- return nil
-}
-
-func chaosParse(c *caddy.Controller) (string, map[string]bool, error) {
- // Set here so we pick up AppName and AppVersion that get set in coremain's init().
- chaosVersion = caddy.AppName + "-" + caddy.AppVersion
-
- version := ""
- authors := make(map[string]bool)
-
- for c.Next() {
- args := c.RemainingArgs()
- if len(args) == 0 {
- return chaosVersion, nil, nil
- }
- if len(args) == 1 {
- return args[0], nil, nil
- }
- version = args[0]
- for _, a := range args[1:] {
- authors[a] = true
- }
- return version, authors, nil
- }
- return version, authors, nil
-}
-
-var chaosVersion string
diff --git a/middleware/chaos/setup_test.go b/middleware/chaos/setup_test.go
deleted file mode 100644
index 6f3c13fb3..000000000
--- a/middleware/chaos/setup_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package chaos
-
-import (
- "strings"
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestSetupChaos(t *testing.T) {
- tests := []struct {
- input string
- shouldErr bool
- expectedVersion string // expected version.
- expectedAuthor string // expected author (string, although we get a map).
- expectedErrContent string // substring from the expected error. Empty for positive cases.
- }{
- // positive
- {
- `chaos v2`, false, "v2", "", "",
- },
- {
- `chaos v3 "Miek Gieben"`, false, "v3", "Miek Gieben", "",
- },
- }
-
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- version, authors, err := chaosParse(c)
-
- if test.shouldErr && err == nil {
- t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input)
- }
-
- if err != nil {
- if !test.shouldErr {
- t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
- }
-
- if !strings.Contains(err.Error(), test.expectedErrContent) {
- t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input)
- }
- }
-
- if !test.shouldErr && version != test.expectedVersion {
- t.Errorf("Chaos not correctly set for input %s. Expected: %s, actual: %s", test.input, test.expectedVersion, version)
- }
- if !test.shouldErr && authors != nil {
- if _, ok := authors[test.expectedAuthor]; !ok {
- t.Errorf("Chaos not correctly set for input %s. Expected: '%s', actual: '%s'", test.input, test.expectedAuthor, "Miek Gieben")
- }
- }
- }
-}
diff --git a/middleware/debug/README.md b/middleware/debug/README.md
deleted file mode 100644
index cae6d0ff8..000000000
--- a/middleware/debug/README.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# debug
-
-*debug* disables the automatic recovery upon a CoreDNS crash so that you'll get a nice stack trace.
-
-Note that the *errors* middleware (if loaded) will also set a `recover` negating this setting.
-The main use of *debug* is to help testing.
-
-## Syntax
-
-~~~ txt
-debug
-~~~
-
-## Examples
-
-Disable CoreDNS' ability to recover from crashes:
-
-~~~ txt
-debug
-~~~
diff --git a/middleware/debug/debug.go b/middleware/debug/debug.go
deleted file mode 100644
index f92a27554..000000000
--- a/middleware/debug/debug.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package debug
-
-import (
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("debug", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- config := dnsserver.GetConfig(c)
-
- for c.Next() {
- if c.NextArg() {
- return middleware.Error("debug", c.ArgErr())
- }
- config.Debug = true
- }
-
- return nil
-}
diff --git a/middleware/debug/debug_test.go b/middleware/debug/debug_test.go
deleted file mode 100644
index a4802fee5..000000000
--- a/middleware/debug/debug_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package debug
-
-import (
- "io/ioutil"
- "log"
- "testing"
-
- "github.com/coredns/coredns/core/dnsserver"
-
- "github.com/mholt/caddy"
-)
-
-func TestDebug(t *testing.T) {
- log.SetOutput(ioutil.Discard)
-
- tests := []struct {
- input string
- shouldErr bool
- expectedDebug bool
- }{
- // positive
- {
- `debug`, false, true,
- },
- // negative
- {
- `debug off`, true, false,
- },
- }
-
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- err := setup(c)
- cfg := dnsserver.GetConfig(c)
-
- if test.shouldErr && err == nil {
- t.Fatalf("Test %d: Expected error but found %s for input %s", i, err, test.input)
- }
-
- if err != nil {
- if !test.shouldErr {
- t.Fatalf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
- }
- }
- if cfg.Debug != test.expectedDebug {
- t.Fatalf("Test %d: Expected debug to be: %t, but got: %t, input: %s", i, test.expectedDebug, cfg.Debug, test.input)
- }
- }
-}
diff --git a/middleware/dnssec/README.md b/middleware/dnssec/README.md
deleted file mode 100644
index 2422ea640..000000000
--- a/middleware/dnssec/README.md
+++ /dev/null
@@ -1,88 +0,0 @@
-# dnssec
-
-*dnssec* enables on-the-fly DNSSEC signing of served data.
-
-## Syntax
-
-~~~
-dnssec [ZONES... ] {
- key file KEY...
- cache_capacity CAPACITY
-}
-~~~
-
-The specified key is used for all signing operations. The DNSSEC signing will treat this key a
-CSK (common signing key), forgoing the ZSK/KSK split. All signing operations are done online.
-Authenticated denial of existence is implemented with NSEC black lies. Using ECDSA as an algorithm
-is preferred as this leads to smaller signatures (compared to RSA). NSEC3 is *not* supported.
-
-If multiple *dnssec* middlewares are specified in the same zone, the last one specified will be
-used ( see [bugs](#bugs) ).
-
-* `ZONES` zones that should be signed. If empty, the zones from the configuration block
- are used.
-
-* `key file` indicates that key file(s) should be read from disk. When multiple keys are specified, RRsets
- will be signed with all keys. Generating a key can be done with `dnssec-keygen`: `dnssec-keygen -a
- ECDSAP256SHA256 <zonename>`. A key created for zone *A* can be safely used for zone *B*. The name of the
- key file can be specified as one of the following formats
-
- * basename of the generated key `Kexample.org+013+45330`
-
- * generated public key `Kexample.org+013+45330.key`
-
- * generated private key `Kexample.org+013+45330.private`
-
-* `cache_capacity` indicates the capacity of the cache. The dnssec middleware uses a cache to store
- RRSIGs. The default capacity is 10000.
-
-## Metrics
-
-If monitoring is enabled (via the *prometheus* directive) then the following metrics are exported:
-
-* coredns_dnssec_cache_size{type} - total elements in the cache, type is "signature".
-* coredns_dnssec_cache_capacity{type} - total capacity of the cache, type is "signature".
-* coredns_dnssec_cache_hits_total - Counter of cache hits.
-* coredns_dnssec_cache_misses_total - Counter of cache misses.
-
-## Examples
-
-Sign responses for `example.org` with the key "Kexample.org.+013+45330.key".
-
-~~~
-example.org:53 {
- dnssec {
- key file /etc/coredns/Kexample.org.+013+45330
- }
- whoami
-}
-~~~
-
-Sign responses for a kubernetes zone with the key "Kcluster.local+013+45129.key".
-
-~~~
-cluster.local:53 {
- kubernetes cluster.local
- dnssec cluster.local {
- key file /etc/coredns/Kcluster.local+013+45129
- }
-}
-~~~
-
-## Bugs
-
-Multiple *dnssec* middlewares inside one server stanza will silently overwrite earlier ones, here
-`example.local` will overwrite the one for `cluster.local`.
-
-~~~
-.:53 {
- kubernetes cluster.local
- dnssec cluster.local {
- key file /etc/coredns/cluster.local
- }
- dnssec example.local {
- key file /etc/coredns/example.local
- }
- whoami
-}
-~~~
diff --git a/middleware/dnssec/black_lies.go b/middleware/dnssec/black_lies.go
deleted file mode 100644
index 527b2fc3e..000000000
--- a/middleware/dnssec/black_lies.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package dnssec
-
-import "github.com/miekg/dns"
-
-// nsec returns an NSEC useful for NXDOMAIN respsones.
-// See https://tools.ietf.org/html/draft-valsorda-dnsop-black-lies-00
-// For example, a request for the non-existing name a.example.com would
-// cause the following NSEC record to be generated:
-// a.example.com. 3600 IN NSEC \000.a.example.com. ( RRSIG NSEC )
-// This inturn makes every NXDOMAIN answer a NODATA one, don't forget to flip
-// the header rcode to NOERROR.
-func (d Dnssec) nsec(name, zone string, ttl, incep, expir uint32) ([]dns.RR, error) {
- nsec := &dns.NSEC{}
- nsec.Hdr = dns.RR_Header{Name: name, Ttl: ttl, Class: dns.ClassINET, Rrtype: dns.TypeNSEC}
- nsec.NextDomain = "\\000." + name
- nsec.TypeBitMap = []uint16{dns.TypeRRSIG, dns.TypeNSEC}
-
- sigs, err := d.sign([]dns.RR{nsec}, zone, ttl, incep, expir)
- if err != nil {
- return nil, err
- }
-
- return append(sigs, nsec), nil
-}
diff --git a/middleware/dnssec/black_lies_test.go b/middleware/dnssec/black_lies_test.go
deleted file mode 100644
index 342e7bb3a..000000000
--- a/middleware/dnssec/black_lies_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package dnssec
-
-import (
- "testing"
- "time"
-
- "github.com/coredns/coredns/middleware/test"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-func TestZoneSigningBlackLies(t *testing.T) {
- d, rm1, rm2 := newDnssec(t, []string{"miek.nl."})
- defer rm1()
- defer rm2()
-
- m := testNxdomainMsg()
- state := request.Request{Req: m}
- m = d.Sign(state, "miek.nl.", time.Now().UTC())
- if !section(m.Ns, 2) {
- t.Errorf("authority section should have 2 sig")
- }
- var nsec *dns.NSEC
- for _, r := range m.Ns {
- if r.Header().Rrtype == dns.TypeNSEC {
- nsec = r.(*dns.NSEC)
- }
- }
- if m.Rcode != dns.RcodeSuccess {
- t.Errorf("expected rcode %d, got %d", dns.RcodeSuccess, m.Rcode)
- }
- if nsec == nil {
- t.Fatalf("expected NSEC, got none")
- }
- if nsec.Hdr.Name != "ww.miek.nl." {
- t.Errorf("expected %s, got %s", "ww.miek.nl.", nsec.Hdr.Name)
- }
- if nsec.NextDomain != "\\000.ww.miek.nl." {
- t.Errorf("expected %s, got %s", "\\000.ww.miek.nl.", nsec.NextDomain)
- }
-}
-
-func testNxdomainMsg() *dns.Msg {
- return &dns.Msg{MsgHdr: dns.MsgHdr{Rcode: dns.RcodeNameError},
- Question: []dns.Question{{Name: "ww.miek.nl.", Qclass: dns.ClassINET, Qtype: dns.TypeTXT}},
- Ns: []dns.RR{test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1461471181 14400 3600 604800 14400")},
- }
-}
diff --git a/middleware/dnssec/cache.go b/middleware/dnssec/cache.go
deleted file mode 100644
index ea95b73b4..000000000
--- a/middleware/dnssec/cache.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package dnssec
-
-import (
- "hash/fnv"
-
- "github.com/miekg/dns"
-)
-
-// hash serializes the RRset and return a signature cache key.
-func hash(rrs []dns.RR) uint32 {
- h := fnv.New32()
- buf := make([]byte, 256)
- for _, r := range rrs {
- off, err := dns.PackRR(r, buf, 0, nil, false)
- if err == nil {
- h.Write(buf[:off])
- }
- }
-
- i := h.Sum32()
- return i
-}
diff --git a/middleware/dnssec/cache_test.go b/middleware/dnssec/cache_test.go
deleted file mode 100644
index 7d42a90df..000000000
--- a/middleware/dnssec/cache_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package dnssec
-
-import (
- "testing"
- "time"
-
- "github.com/coredns/coredns/middleware/pkg/cache"
- "github.com/coredns/coredns/middleware/test"
- "github.com/coredns/coredns/request"
-)
-
-func TestCacheSet(t *testing.T) {
- fPriv, rmPriv, _ := test.TempFile(".", privKey)
- fPub, rmPub, _ := test.TempFile(".", pubKey)
- defer rmPriv()
- defer rmPub()
-
- dnskey, err := ParseKeyFile(fPub, fPriv)
- if err != nil {
- t.Fatalf("failed to parse key: %v\n", err)
- }
-
- c := cache.New(defaultCap)
- m := testMsg()
- state := request.Request{Req: m}
- k := hash(m.Answer) // calculate *before* we add the sig
- d := New([]string{"miek.nl."}, []*DNSKEY{dnskey}, nil, c)
- d.Sign(state, "miek.nl.", time.Now().UTC())
-
- _, ok := d.get(k)
- if !ok {
- t.Errorf("signature was not added to the cache")
- }
-}
diff --git a/middleware/dnssec/dnskey.go b/middleware/dnssec/dnskey.go
deleted file mode 100644
index ce787ab54..000000000
--- a/middleware/dnssec/dnskey.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package dnssec
-
-import (
- "crypto"
- "crypto/ecdsa"
- "crypto/rsa"
- "errors"
- "os"
- "time"
-
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-// DNSKEY holds a DNSSEC public and private key used for on-the-fly signing.
-type DNSKEY struct {
- K *dns.DNSKEY
- s crypto.Signer
- keytag uint16
-}
-
-// ParseKeyFile read a DNSSEC keyfile as generated by dnssec-keygen or other
-// utilities. It adds ".key" for the public key and ".private" for the private key.
-func ParseKeyFile(pubFile, privFile string) (*DNSKEY, error) {
- f, e := os.Open(pubFile)
- if e != nil {
- return nil, e
- }
- k, e := dns.ReadRR(f, pubFile)
- if e != nil {
- return nil, e
- }
-
- f, e = os.Open(privFile)
- if e != nil {
- return nil, e
- }
- p, e := k.(*dns.DNSKEY).ReadPrivateKey(f, privFile)
- if e != nil {
- return nil, e
- }
-
- if v, ok := p.(*rsa.PrivateKey); ok {
- return &DNSKEY{k.(*dns.DNSKEY), v, k.(*dns.DNSKEY).KeyTag()}, nil
- }
- if v, ok := p.(*ecdsa.PrivateKey); ok {
- return &DNSKEY{k.(*dns.DNSKEY), v, k.(*dns.DNSKEY).KeyTag()}, nil
- }
- return &DNSKEY{k.(*dns.DNSKEY), nil, 0}, errors.New("no known? private key found")
-}
-
-// getDNSKEY returns the correct DNSKEY to the client. Signatures are added when do is true.
-func (d Dnssec) getDNSKEY(state request.Request, zone string, do bool) *dns.Msg {
- keys := make([]dns.RR, len(d.keys))
- for i, k := range d.keys {
- keys[i] = dns.Copy(k.K)
- keys[i].Header().Name = zone
- }
- m := new(dns.Msg)
- m.SetReply(state.Req)
- m.Answer = keys
- if !do {
- return m
- }
-
- incep, expir := incepExpir(time.Now().UTC())
- if sigs, err := d.sign(keys, zone, 3600, incep, expir); err == nil {
- m.Answer = append(m.Answer, sigs...)
- }
- return m
-}
diff --git a/middleware/dnssec/dnssec.go b/middleware/dnssec/dnssec.go
deleted file mode 100644
index 4e1e70217..000000000
--- a/middleware/dnssec/dnssec.go
+++ /dev/null
@@ -1,135 +0,0 @@
-// Package dnssec implements a middleware that signs responses on-the-fly using
-// NSEC black lies.
-package dnssec
-
-import (
- "time"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/cache"
- "github.com/coredns/coredns/middleware/pkg/response"
- "github.com/coredns/coredns/middleware/pkg/singleflight"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-// Dnssec signs the reply on-the-fly.
-type Dnssec struct {
- Next middleware.Handler
-
- zones []string
- keys []*DNSKEY
- inflight *singleflight.Group
- cache *cache.Cache
-}
-
-// New returns a new Dnssec.
-func New(zones []string, keys []*DNSKEY, next middleware.Handler, c *cache.Cache) Dnssec {
- return Dnssec{Next: next,
- zones: zones,
- keys: keys,
- cache: c,
- inflight: new(singleflight.Group),
- }
-}
-
-// Sign signs the message in state. it takes care of negative or nodata responses. It
-// uses NSEC black lies for authenticated denial of existence. Signatures
-// creates will be cached for a short while. By default we sign for 8 days,
-// starting 3 hours ago.
-func (d Dnssec) Sign(state request.Request, zone string, now time.Time) *dns.Msg {
- req := state.Req
-
- mt, _ := response.Typify(req, time.Now().UTC()) // TODO(miek): need opt record here?
- if mt == response.Delegation {
- // TODO(miek): uh, signing DS record?!?!
- return req
- }
-
- incep, expir := incepExpir(now)
-
- if mt == response.NameError {
- if req.Ns[0].Header().Rrtype != dns.TypeSOA || len(req.Ns) > 1 {
- return req
- }
-
- ttl := req.Ns[0].Header().Ttl
-
- if sigs, err := d.sign(req.Ns, zone, ttl, incep, expir); err == nil {
- req.Ns = append(req.Ns, sigs...)
- }
- if sigs, err := d.nsec(state.Name(), zone, ttl, incep, expir); err == nil {
- req.Ns = append(req.Ns, sigs...)
- }
- if len(req.Ns) > 1 { // actually added nsec and sigs, reset the rcode
- req.Rcode = dns.RcodeSuccess
- }
- return req
- }
-
- for _, r := range rrSets(req.Answer) {
- ttl := r[0].Header().Ttl
- if sigs, err := d.sign(r, zone, ttl, incep, expir); err == nil {
- req.Answer = append(req.Answer, sigs...)
- }
- }
- for _, r := range rrSets(req.Ns) {
- ttl := r[0].Header().Ttl
- if sigs, err := d.sign(r, zone, ttl, incep, expir); err == nil {
- req.Ns = append(req.Ns, sigs...)
- }
- }
- for _, r := range rrSets(req.Extra) {
- ttl := r[0].Header().Ttl
- if sigs, err := d.sign(r, zone, ttl, incep, expir); err == nil {
- req.Extra = append(sigs, req.Extra...) // prepend to leave OPT alone
- }
- }
- return req
-}
-
-func (d Dnssec) sign(rrs []dns.RR, signerName string, ttl, incep, expir uint32) ([]dns.RR, error) {
- k := hash(rrs)
- sgs, ok := d.get(k)
- if ok {
- return sgs, nil
- }
-
- sigs, err := d.inflight.Do(k, func() (interface{}, error) {
- sigs := make([]dns.RR, len(d.keys))
- var e error
- for i, k := range d.keys {
- sig := k.newRRSIG(signerName, ttl, incep, expir)
- e = sig.Sign(k.s, rrs)
- sigs[i] = sig
- }
- d.set(k, sigs)
- return sigs, e
- })
- return sigs.([]dns.RR), err
-}
-
-func (d Dnssec) set(key uint32, sigs []dns.RR) {
- d.cache.Add(key, sigs)
-}
-
-func (d Dnssec) get(key uint32) ([]dns.RR, bool) {
- if s, ok := d.cache.Get(key); ok {
- cacheHits.Inc()
- return s.([]dns.RR), true
- }
- cacheMisses.Inc()
- return nil, false
-}
-
-func incepExpir(now time.Time) (uint32, uint32) {
- incep := uint32(now.Add(-3 * time.Hour).Unix()) // -(2+1) hours, be sure to catch daylight saving time and such
- expir := uint32(now.Add(eightDays).Unix()) // sign for 8 days
- return incep, expir
-}
-
-const (
- eightDays = 8 * 24 * time.Hour
- defaultCap = 10000 // default capacity of the cache.
-)
diff --git a/middleware/dnssec/dnssec_test.go b/middleware/dnssec/dnssec_test.go
deleted file mode 100644
index 3549a7c8f..000000000
--- a/middleware/dnssec/dnssec_test.go
+++ /dev/null
@@ -1,219 +0,0 @@
-package dnssec
-
-import (
- "testing"
- "time"
-
- "github.com/coredns/coredns/middleware/pkg/cache"
- "github.com/coredns/coredns/middleware/test"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-func TestZoneSigning(t *testing.T) {
- d, rm1, rm2 := newDnssec(t, []string{"miek.nl."})
- defer rm1()
- defer rm2()
-
- m := testMsg()
- state := request.Request{Req: m}
-
- m = d.Sign(state, "miek.nl.", time.Now().UTC())
- if !section(m.Answer, 1) {
- t.Errorf("answer section should have 1 sig")
- }
- if !section(m.Ns, 1) {
- t.Errorf("authority section should have 1 sig")
- }
-}
-
-func TestZoneSigningDouble(t *testing.T) {
- d, rm1, rm2 := newDnssec(t, []string{"miek.nl."})
- defer rm1()
- defer rm2()
-
- fPriv1, rmPriv1, _ := test.TempFile(".", privKey1)
- fPub1, rmPub1, _ := test.TempFile(".", pubKey1)
- defer rmPriv1()
- defer rmPub1()
-
- key1, err := ParseKeyFile(fPub1, fPriv1)
- if err != nil {
- t.Fatalf("failed to parse key: %v\n", err)
- }
- d.keys = append(d.keys, key1)
-
- m := testMsg()
- state := request.Request{Req: m}
- m = d.Sign(state, "miek.nl.", time.Now().UTC())
- if !section(m.Answer, 2) {
- t.Errorf("answer section should have 1 sig")
- }
- if !section(m.Ns, 2) {
- t.Errorf("authority section should have 1 sig")
- }
-}
-
-// TestSigningDifferentZone tests if a key for miek.nl and be used for example.org.
-func TestSigningDifferentZone(t *testing.T) {
- fPriv, rmPriv, _ := test.TempFile(".", privKey)
- fPub, rmPub, _ := test.TempFile(".", pubKey)
- defer rmPriv()
- defer rmPub()
-
- key, err := ParseKeyFile(fPub, fPriv)
- if err != nil {
- t.Fatalf("failed to parse key: %v\n", err)
- }
-
- m := testMsgEx()
- state := request.Request{Req: m}
- c := cache.New(defaultCap)
- d := New([]string{"example.org."}, []*DNSKEY{key}, nil, c)
- m = d.Sign(state, "example.org.", time.Now().UTC())
- if !section(m.Answer, 1) {
- t.Errorf("answer section should have 1 sig")
- t.Logf("%+v\n", m)
- }
- if !section(m.Ns, 1) {
- t.Errorf("authority section should have 1 sig")
- t.Logf("%+v\n", m)
- }
-}
-
-func TestSigningCname(t *testing.T) {
- d, rm1, rm2 := newDnssec(t, []string{"miek.nl."})
- defer rm1()
- defer rm2()
-
- m := testMsgCname()
- state := request.Request{Req: m}
- m = d.Sign(state, "miek.nl.", time.Now().UTC())
- if !section(m.Answer, 1) {
- t.Errorf("answer section should have 1 sig")
- }
-}
-
-func TestZoneSigningDelegation(t *testing.T) {
- d, rm1, rm2 := newDnssec(t, []string{"miek.nl."})
- defer rm1()
- defer rm2()
-
- m := testDelegationMsg()
- state := request.Request{Req: m}
- m = d.Sign(state, "miek.nl.", time.Now().UTC())
- if !section(m.Ns, 0) {
- t.Errorf("authority section should have 0 sig")
- t.Logf("%v\n", m)
- }
- if !section(m.Extra, 0) {
- t.Errorf("answer section should have 0 sig")
- t.Logf("%v\n", m)
- }
-}
-
-func TestSigningDname(t *testing.T) {
- d, rm1, rm2 := newDnssec(t, []string{"miek.nl."})
- defer rm1()
- defer rm2()
-
- m := testMsgDname()
- state := request.Request{Req: m}
- // We sign *everything* we see, also the synthesized CNAME.
- m = d.Sign(state, "miek.nl.", time.Now().UTC())
- if !section(m.Answer, 3) {
- t.Errorf("answer section should have 3 sig")
- }
-}
-
-func section(rss []dns.RR, nrSigs int) bool {
- i := 0
- for _, r := range rss {
- if r.Header().Rrtype == dns.TypeRRSIG {
- i++
- }
- }
- return nrSigs == i
-}
-
-func testMsg() *dns.Msg {
- // don't care about the message header
- return &dns.Msg{
- Answer: []dns.RR{test.MX("miek.nl. 1703 IN MX 1 aspmx.l.google.com.")},
- Ns: []dns.RR{test.NS("miek.nl. 1703 IN NS omval.tednet.nl.")},
- }
-}
-func testMsgEx() *dns.Msg {
- return &dns.Msg{
- Answer: []dns.RR{test.MX("example.org. 1703 IN MX 1 aspmx.l.google.com.")},
- Ns: []dns.RR{test.NS("example.org. 1703 IN NS omval.tednet.nl.")},
- }
-}
-
-func testMsgCname() *dns.Msg {
- return &dns.Msg{
- Answer: []dns.RR{test.CNAME("www.miek.nl. 1800 IN CNAME a.miek.nl.")},
- }
-}
-
-func testDelegationMsg() *dns.Msg {
- return &dns.Msg{
- Ns: []dns.RR{
- test.NS("miek.nl. 3600 IN NS linode.atoom.net."),
- test.NS("miek.nl. 3600 IN NS ns-ext.nlnetlabs.nl."),
- test.NS("miek.nl. 3600 IN NS omval.tednet.nl."),
- },
- Extra: []dns.RR{
- test.A("omval.tednet.nl. 3600 IN A 185.49.141.42"),
- test.AAAA("omval.tednet.nl. 3600 IN AAAA 2a04:b900:0:100::42"),
- },
- }
-}
-
-func testMsgDname() *dns.Msg {
- return &dns.Msg{
- Answer: []dns.RR{
- test.CNAME("a.dname.miek.nl. 1800 IN CNAME a.test.miek.nl."),
- test.A("a.test.miek.nl. 1800 IN A 139.162.196.78"),
- test.DNAME("dname.miek.nl. 1800 IN DNAME test.miek.nl."),
- },
- }
-}
-
-func newDnssec(t *testing.T, zones []string) (Dnssec, func(), func()) {
- k, rm1, rm2 := newKey(t)
- c := cache.New(defaultCap)
- d := New(zones, []*DNSKEY{k}, nil, c)
- return d, rm1, rm2
-}
-
-func newKey(t *testing.T) (*DNSKEY, func(), func()) {
- fPriv, rmPriv, _ := test.TempFile(".", privKey)
- fPub, rmPub, _ := test.TempFile(".", pubKey)
-
- key, err := ParseKeyFile(fPub, fPriv)
- if err != nil {
- t.Fatalf("failed to parse key: %v\n", err)
- }
- return key, rmPriv, rmPub
-}
-
-const (
- pubKey = `miek.nl. IN DNSKEY 257 3 13 0J8u0XJ9GNGFEBXuAmLu04taHG4BXPP3gwhetiOUMnGA+x09nqzgF5IY OyjWB7N3rXqQbnOSILhH1hnuyh7mmA==`
- privKey = `Private-key-format: v1.3
-Algorithm: 13 (ECDSAP256SHA256)
-PrivateKey: /4BZk8AFvyW5hL3cOLSVxIp1RTqHSAEloWUxj86p3gs=
-Created: 20160423195532
-Publish: 20160423195532
-Activate: 20160423195532
-`
- pubKey1 = `example.org. IN DNSKEY 257 3 13 tVRWNSGpHZbCi7Pr7OmbADVUO3MxJ0Lb8Lk3o/HBHqCxf5K/J50lFqRa 98lkdAIiFOVRy8LyMvjwmxZKwB5MNw==`
- privKey1 = `Private-key-format: v1.3
-Algorithm: 13 (ECDSAP256SHA256)
-PrivateKey: i8j4OfDGT8CQt24SDwLz2hg9yx4qKOEOh1LvbAuSp1c=
-Created: 20160423211746
-Publish: 20160423211746
-Activate: 20160423211746
-`
-)
diff --git a/middleware/dnssec/handler.go b/middleware/dnssec/handler.go
deleted file mode 100644
index dab02e469..000000000
--- a/middleware/dnssec/handler.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package dnssec
-
-import (
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "github.com/prometheus/client_golang/prometheus"
- "golang.org/x/net/context"
-)
-
-// ServeDNS implements the middleware.Handler interface.
-func (d Dnssec) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- state := request.Request{W: w, Req: r}
-
- do := state.Do()
- qname := state.Name()
- qtype := state.QType()
- zone := middleware.Zones(d.zones).Matches(qname)
- if zone == "" {
- return middleware.NextOrFailure(d.Name(), d.Next, ctx, w, r)
- }
-
- // Intercept queries for DNSKEY, but only if one of the zones matches the qname, otherwise we let
- // the query through.
- if qtype == dns.TypeDNSKEY {
- for _, z := range d.zones {
- if qname == z {
- resp := d.getDNSKEY(state, z, do)
- resp.Authoritative = true
- state.SizeAndDo(resp)
- w.WriteMsg(resp)
- return dns.RcodeSuccess, nil
- }
- }
- }
-
- drr := &ResponseWriter{w, d}
- return middleware.NextOrFailure(d.Name(), d.Next, ctx, drr, r)
-}
-
-var (
- cacheSize = prometheus.NewGaugeVec(prometheus.GaugeOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "cache_size",
- Help: "The number of elements in the dnssec cache.",
- }, []string{"type"})
-
- cacheCapacity = prometheus.NewGaugeVec(prometheus.GaugeOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "cache_capacity",
- Help: "The dnssec cache's capacity.",
- }, []string{"type"})
-
- cacheHits = prometheus.NewCounter(prometheus.CounterOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "cache_hits_total",
- Help: "The count of cache hits.",
- })
-
- cacheMisses = prometheus.NewCounter(prometheus.CounterOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "cache_misses_total",
- Help: "The count of cache misses.",
- })
-)
-
-// Name implements the Handler interface.
-func (d Dnssec) Name() string { return "dnssec" }
-
-const subsystem = "dnssec"
-
-func init() {
- prometheus.MustRegister(cacheSize)
- prometheus.MustRegister(cacheCapacity)
- prometheus.MustRegister(cacheHits)
- prometheus.MustRegister(cacheMisses)
-}
diff --git a/middleware/dnssec/handler_test.go b/middleware/dnssec/handler_test.go
deleted file mode 100644
index 19d0ef01f..000000000
--- a/middleware/dnssec/handler_test.go
+++ /dev/null
@@ -1,155 +0,0 @@
-package dnssec
-
-import (
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/file"
- "github.com/coredns/coredns/middleware/pkg/cache"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-var dnssecTestCases = []test.Case{
- {
- Qname: "miek.nl.", Qtype: dns.TypeDNSKEY,
- Answer: []dns.RR{
- test.DNSKEY("miek.nl. 3600 IN DNSKEY 257 3 13 0J8u0XJ9GNGFEBXuAmLu04taHG4"),
- },
- },
- {
- Qname: "miek.nl.", Qtype: dns.TypeDNSKEY, Do: true,
- Answer: []dns.RR{
- test.DNSKEY("miek.nl. 3600 IN DNSKEY 257 3 13 0J8u0XJ9GNGFEBXuAmLu04taHG4"),
- test.RRSIG("miek.nl. 3600 IN RRSIG DNSKEY 13 2 3600 20160503150844 20160425120844 18512 miek.nl. Iw/kNOyM"),
- },
- Extra: []dns.RR{test.OPT(4096, true)},
- },
-}
-
-var dnsTestCases = []test.Case{
- {
- Qname: "miek.nl.", Qtype: dns.TypeDNSKEY,
- Answer: []dns.RR{
- test.DNSKEY("miek.nl. 3600 IN DNSKEY 257 3 13 0J8u0XJ9GNGFEBXuAmLu04taHG4"),
- },
- },
- {
- Qname: "miek.nl.", Qtype: dns.TypeMX,
- Answer: []dns.RR{
- test.MX("miek.nl. 1800 IN MX 1 aspmx.l.google.com."),
- },
- Ns: []dns.RR{
- test.NS("miek.nl. 1800 IN NS linode.atoom.net."),
- },
- },
- {
- Qname: "miek.nl.", Qtype: dns.TypeMX, Do: true,
- Answer: []dns.RR{
- test.MX("miek.nl. 1800 IN MX 1 aspmx.l.google.com."),
- test.RRSIG("miek.nl. 1800 IN RRSIG MX 13 2 3600 20160503192428 20160425162428 18512 miek.nl. 4nxuGKitXjPVA9zP1JIUvA09"),
- },
- Ns: []dns.RR{
- test.NS("miek.nl. 1800 IN NS linode.atoom.net."),
- test.RRSIG("miek.nl. 1800 IN RRSIG NS 13 2 3600 20161217114912 20161209084912 18512 miek.nl. ad9gA8VWgF1H8ze9/0Rk2Q=="),
- },
- Extra: []dns.RR{test.OPT(4096, true)},
- },
- {
- Qname: "www.miek.nl.", Qtype: dns.TypeAAAA, Do: true,
- Answer: []dns.RR{
- test.AAAA("a.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- test.RRSIG("a.miek.nl. 1800 IN RRSIG AAAA 13 3 3600 20160503193047 20160425163047 18512 miek.nl. UAyMG+gcnoXW3"),
- test.CNAME("www.miek.nl. 1800 IN CNAME a.miek.nl."),
- test.RRSIG("www.miek.nl. 1800 IN RRSIG CNAME 13 3 3600 20160503193047 20160425163047 18512 miek.nl. E3qGZn"),
- },
- Ns: []dns.RR{
- test.NS("miek.nl. 1800 IN NS linode.atoom.net."),
- test.RRSIG("miek.nl. 1800 IN RRSIG NS 13 2 3600 20161217114912 20161209084912 18512 miek.nl. ad9gA8VWgF1H8ze9/0Rk2Q=="),
- },
- Extra: []dns.RR{test.OPT(4096, true)},
- },
- {
- Qname: "www.example.org.", Qtype: dns.TypeAAAA, Do: true,
- Rcode: dns.RcodeServerFailure,
- // Extra: []dns.RR{test.OPT(4096, true)}, // test.ErrorHandler is a simple handler that does not do EDNS.
- },
-}
-
-func TestLookupZone(t *testing.T) {
- zone, err := file.Parse(strings.NewReader(dbMiekNL), "miek.nl.", "stdin", 0)
- if err != nil {
- return
- }
- fm := file.File{Next: test.ErrorHandler(), Zones: file.Zones{Z: map[string]*file.Zone{"miek.nl.": zone}, Names: []string{"miek.nl."}}}
- dnskey, rm1, rm2 := newKey(t)
- defer rm1()
- defer rm2()
- c := cache.New(defaultCap)
- dh := New([]string{"miek.nl."}, []*DNSKEY{dnskey}, fm, c)
- ctx := context.TODO()
-
- for _, tc := range dnsTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := dh.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-func TestLookupDNSKEY(t *testing.T) {
- dnskey, rm1, rm2 := newKey(t)
- defer rm1()
- defer rm2()
- c := cache.New(defaultCap)
- dh := New([]string{"miek.nl."}, []*DNSKEY{dnskey}, test.ErrorHandler(), c)
- ctx := context.TODO()
-
- for _, tc := range dnssecTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := dh.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- if !resp.Authoritative {
- t.Errorf("Authoritative Answer should be true, got false")
- }
-
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-const dbMiekNL = `
-$TTL 30M
-$ORIGIN miek.nl.
-@ IN SOA linode.atoom.net. miek.miek.nl. (
- 1282630057 ; Serial
- 4H ; Refresh
- 1H ; Retry
- 7D ; Expire
- 4H ) ; Negative Cache TTL
- IN NS linode.atoom.net.
-
- IN MX 1 aspmx.l.google.com.
-
- IN A 139.162.196.78
- IN AAAA 2a01:7e00::f03c:91ff:fef1:6735
-
-a IN A 139.162.196.78
- IN AAAA 2a01:7e00::f03c:91ff:fef1:6735
-www IN CNAME a`
diff --git a/middleware/dnssec/responsewriter.go b/middleware/dnssec/responsewriter.go
deleted file mode 100644
index d3c34d114..000000000
--- a/middleware/dnssec/responsewriter.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package dnssec
-
-import (
- "log"
- "time"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-// ResponseWriter sign the response on the fly.
-type ResponseWriter struct {
- dns.ResponseWriter
- d Dnssec
-}
-
-// WriteMsg implements the dns.ResponseWriter interface.
-func (d *ResponseWriter) WriteMsg(res *dns.Msg) error {
- // By definition we should sign anything that comes back, we should still figure out for
- // which zone it should be.
- state := request.Request{W: d.ResponseWriter, Req: res}
-
- qname := state.Name()
- zone := middleware.Zones(d.d.zones).Matches(qname)
- if zone == "" {
- return d.ResponseWriter.WriteMsg(res)
- }
-
- if state.Do() {
- res = d.d.Sign(state, zone, time.Now().UTC())
-
- cacheSize.WithLabelValues("signature").Set(float64(d.d.cache.Len()))
- }
- state.SizeAndDo(res)
-
- return d.ResponseWriter.WriteMsg(res)
-}
-
-// Write implements the dns.ResponseWriter interface.
-func (d *ResponseWriter) Write(buf []byte) (int, error) {
- log.Printf("[WARNING] Dnssec called with Write: not signing reply")
- n, err := d.ResponseWriter.Write(buf)
- return n, err
-}
-
-// Hijack implements the dns.ResponseWriter interface.
-func (d *ResponseWriter) Hijack() { d.ResponseWriter.Hijack() }
diff --git a/middleware/dnssec/rrsig.go b/middleware/dnssec/rrsig.go
deleted file mode 100644
index c68413622..000000000
--- a/middleware/dnssec/rrsig.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package dnssec
-
-import "github.com/miekg/dns"
-
-// newRRSIG return a new RRSIG, with all fields filled out, except the signed data.
-func (k *DNSKEY) newRRSIG(signerName string, ttl, incep, expir uint32) *dns.RRSIG {
- sig := new(dns.RRSIG)
-
- sig.Hdr.Rrtype = dns.TypeRRSIG
- sig.Algorithm = k.K.Algorithm
- sig.KeyTag = k.keytag
- sig.SignerName = signerName
- sig.Hdr.Ttl = ttl
- sig.OrigTtl = origTTL
-
- sig.Inception = incep
- sig.Expiration = expir
-
- return sig
-}
-
-type rrset struct {
- qname string
- qtype uint16
-}
-
-// rrSets returns rrs as a map of RRsets. It skips RRSIG and OPT records as those don't need to be signed.
-func rrSets(rrs []dns.RR) map[rrset][]dns.RR {
- m := make(map[rrset][]dns.RR)
-
- for _, r := range rrs {
- if r.Header().Rrtype == dns.TypeRRSIG || r.Header().Rrtype == dns.TypeOPT {
- continue
- }
-
- if s, ok := m[rrset{r.Header().Name, r.Header().Rrtype}]; ok {
- s = append(s, r)
- m[rrset{r.Header().Name, r.Header().Rrtype}] = s
- continue
- }
-
- s := make([]dns.RR, 1, 3)
- s[0] = r
- m[rrset{r.Header().Name, r.Header().Rrtype}] = s
- }
-
- if len(m) > 0 {
- return m
- }
- return nil
-}
-
-const origTTL = 3600
diff --git a/middleware/dnssec/setup.go b/middleware/dnssec/setup.go
deleted file mode 100644
index 90425b711..000000000
--- a/middleware/dnssec/setup.go
+++ /dev/null
@@ -1,128 +0,0 @@
-package dnssec
-
-import (
- "fmt"
- "strconv"
- "strings"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/cache"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("dnssec", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- zones, keys, capacity, err := dnssecParse(c)
- if err != nil {
- return middleware.Error("dnssec", err)
- }
-
- ca := cache.New(capacity)
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- return New(zones, keys, next, ca)
- })
-
- // Export the capacity for the metrics. This only happens once, because this is a re-load change only.
- cacheCapacity.WithLabelValues("signature").Set(float64(capacity))
-
- return nil
-}
-
-func dnssecParse(c *caddy.Controller) ([]string, []*DNSKEY, int, error) {
- zones := []string{}
-
- keys := []*DNSKEY{}
-
- capacity := defaultCap
- for c.Next() {
- // dnssec [zones...]
- zones = make([]string, len(c.ServerBlockKeys))
- copy(zones, c.ServerBlockKeys)
- args := c.RemainingArgs()
- if len(args) > 0 {
- zones = args
- }
-
- for c.NextBlock() {
- switch c.Val() {
- case "key":
- k, e := keyParse(c)
- if e != nil {
- return nil, nil, 0, e
- }
- keys = append(keys, k...)
- case "cache_capacity":
- if !c.NextArg() {
- return nil, nil, 0, c.ArgErr()
- }
- value := c.Val()
- cacheCap, err := strconv.Atoi(value)
- if err != nil {
- return nil, nil, 0, err
- }
- capacity = cacheCap
- }
-
- }
- }
- for i := range zones {
- zones[i] = middleware.Host(zones[i]).Normalize()
- }
-
- // Check if each keys owner name can actually sign the zones we want them to sign
- for _, k := range keys {
- kname := middleware.Name(k.K.Header().Name)
- ok := false
- for i := range zones {
- if kname.Matches(zones[i]) {
- ok = true
- break
- }
- }
- if !ok {
- return zones, keys, capacity, fmt.Errorf("key %s (keyid: %d) can not sign any of the zones", string(kname), k.keytag)
- }
- }
-
- return zones, keys, capacity, nil
-}
-
-func keyParse(c *caddy.Controller) ([]*DNSKEY, error) {
- keys := []*DNSKEY{}
-
- if !c.NextArg() {
- return nil, c.ArgErr()
- }
- value := c.Val()
- if value == "file" {
- ks := c.RemainingArgs()
- if len(ks) == 0 {
- return nil, c.ArgErr()
- }
-
- for _, k := range ks {
- base := k
- // Kmiek.nl.+013+26205.key, handle .private or without extension: Kmiek.nl.+013+26205
- if strings.HasSuffix(k, ".key") {
- base = k[:len(k)-4]
- }
- if strings.HasSuffix(k, ".private") {
- base = k[:len(k)-8]
- }
- k, err := ParseKeyFile(base+".key", base+".private")
- if err != nil {
- return nil, err
- }
- keys = append(keys, k)
- }
- }
- return keys, nil
-}
diff --git a/middleware/dnssec/setup_test.go b/middleware/dnssec/setup_test.go
deleted file mode 100644
index 99a71279d..000000000
--- a/middleware/dnssec/setup_test.go
+++ /dev/null
@@ -1,120 +0,0 @@
-package dnssec
-
-import (
- "io/ioutil"
- "os"
- "strings"
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestSetupDnssec(t *testing.T) {
- if err := ioutil.WriteFile("Kcluster.local.key", []byte(keypub), 0644); err != nil {
- t.Fatalf("Failed to write pub key file: %s", err)
- }
- defer func() { os.Remove("Kcluster.local.key") }()
- if err := ioutil.WriteFile("Kcluster.local.private", []byte(keypriv), 0644); err != nil {
- t.Fatalf("Failed to write private key file: %s", err)
- }
- defer func() { os.Remove("Kcluster.local.private") }()
-
- tests := []struct {
- input string
- shouldErr bool
- expectedZones []string
- expectedKeys []string
- expectedCapacity int
- expectedErrContent string
- }{
- {`dnssec`, false, nil, nil, defaultCap, ""},
- {`dnssec example.org`, false, []string{"example.org."}, nil, defaultCap, ""},
- {`dnssec 10.0.0.0/8`, false, []string{"10.in-addr.arpa."}, nil, defaultCap, ""},
- {
- `dnssec example.org {
- cache_capacity 100
- }`, false, []string{"example.org."}, nil, 100, "",
- },
- {
- `dnssec cluster.local {
- key file Kcluster.local
- }`, false, []string{"cluster.local."}, nil, defaultCap, "",
- },
- {
- `dnssec example.org cluster.local {
- key file Kcluster.local
- }`, false, []string{"example.org.", "cluster.local."}, nil, defaultCap, "",
- },
- // fails
- {
- `dnssec example.org {
- key file Kcluster.local
- }`, true, []string{"example.org."}, nil, defaultCap, "can not sign any",
- },
- {
- `dnssec example.org {
- key
- }`, true, []string{"example.org."}, nil, defaultCap, "argument count",
- },
- {
- `dnssec example.org {
- key file
- }`, true, []string{"example.org."}, nil, defaultCap, "argument count",
- },
- }
-
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- zones, keys, capacity, err := dnssecParse(c)
-
- if test.shouldErr && err == nil {
- t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input)
- }
-
- if err != nil {
- if !test.shouldErr {
- t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
- }
-
- if !strings.Contains(err.Error(), test.expectedErrContent) {
- t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input)
- }
- }
- if !test.shouldErr {
- for i, z := range test.expectedZones {
- if zones[i] != z {
- t.Errorf("Dnssec not correctly set for input %s. Expected: %s, actual: %s", test.input, z, zones[i])
- }
- }
- for i, k := range test.expectedKeys {
- if k != keys[i].K.Header().Name {
- t.Errorf("Dnssec not correctly set for input %s. Expected: '%s', actual: '%s'", test.input, k, keys[i].K.Header().Name)
- }
- }
- if capacity != test.expectedCapacity {
- t.Errorf("Dnssec not correctly set capacity for input '%s' Expected: '%d', actual: '%d'", test.input, capacity, test.expectedCapacity)
- }
- }
- }
-}
-
-const keypub = `; This is a zone-signing key, keyid 45330, for cluster.local.
-; Created: 20170901060531 (Fri Sep 1 08:05:31 2017)
-; Publish: 20170901060531 (Fri Sep 1 08:05:31 2017)
-; Activate: 20170901060531 (Fri Sep 1 08:05:31 2017)
-cluster.local. IN DNSKEY 256 3 5 AwEAAcFpDv+Cb23kFJowu+VU++b2N1uEHi6Ll9H0BzLasFOdJjEEclCO q/KlD4682vOMXxJNN8ZwOyiCa7Y0TEYqSwWvhHyn3bHCwuy4I6fss4Wd 7Y9dU+6QTgJ8LimGG40Iizjc9zqoU8Q+q81vIukpYWOHioHoY7hsWBvS RSlzDJk3`
-
-const keypriv = `Private-key-format: v1.3
-Algorithm: 5 (RSASHA1)
-Modulus: wWkO/4JvbeQUmjC75VT75vY3W4QeLouX0fQHMtqwU50mMQRyUI6r8qUPjrza84xfEk03xnA7KIJrtjRMRipLBa+EfKfdscLC7Lgjp+yzhZ3tj11T7pBOAnwuKYYbjQiLONz3OqhTxD6rzW8i6SlhY4eKgehjuGxYG9JFKXMMmTc=
-PublicExponent: AQAB
-PrivateExponent: K5XyZFBPrjMVFX5gCZlyPyVDamNGrfSVXSIiMSqpS96BSdCXtmHAjCj4bZFPwkzi6+vs4tJN8p4ZifEVM0a6qwPZyENBrc2qbsweOXE6l8BaPVWFX30xvVRzGXuNtXxlBXE17zoHty5r5mRyRou1bc2HUS5otdkEjE30RiocQVk=
-Prime1: 7RRFUxaZkVNVH1DaT/SV5Sb8kABB389qLwU++argeDCVf+Wm9BBlTrsz2U6bKlfpaUmYZKtCCd+CVxqzMyuu0w==
-Prime2: 0NiY3d7Fa08IGY9L4TaFc02A721YcDNBBf95BP31qGvwnYsLFM/1xZwaEsIjohg8g+m/GpyIlvNMbK6pywIVjQ==
-Exponent1: XjXO8pype9mMmvwrNNix9DTQ6nxfsQugW30PMHGZ78kGr6NX++bEC0xS50jYWjRDGcbYGzD+9iNujSScD3qNZw==
-Exponent2: wkoOhLIfhUIj7etikyUup2Ld5WAbW15DSrotstg0NrgcQ+Q7reP96BXeJ79WeREFE09cyvv/EjdLzPv81/CbbQ==
-Coefficient: ah4LL0KLTO8kSKHK+X9Ud8grYi94QSNdbX11ge/eFcS/41QhDuZRTAFv4y0+IG+VWd+XzojLsQs+jzLe5GzINg==
-Created: 20170901060531
-Publish: 20170901060531
-Activate: 20170901060531
-`
diff --git a/middleware/dnstap/README.md b/middleware/dnstap/README.md
deleted file mode 100644
index d91c9422c..000000000
--- a/middleware/dnstap/README.md
+++ /dev/null
@@ -1,61 +0,0 @@
-# dnstap
-
-*dnstap* enables logging to dnstap, a flexible, structured binary log format for DNS software: http://dnstap.info.
-
-There is a buffer, expect at least 13 requests before the server sends its dnstap messages to the socket.
-
-## Syntax
-
-~~~ txt
-dnstap SOCKET [full]
-~~~
-
-* **SOCKET** is the socket path supplied to the dnstap command line tool.
-* `full` to include the wire-format DNS message.
-
-## Examples
-
-Log information about client requests and responses to */tmp/dnstap.sock*.
-
-~~~ txt
-dnstap /tmp/dnstap.sock
-~~~
-
-Log information including the wire-format DNS message about client requests and responses to */tmp/dnstap.sock*.
-
-~~~ txt
-dnstap unix:///tmp/dnstap.sock full
-~~~
-
-Log to a remote endpoint.
-
-~~~ txt
-dnstap tcp://127.0.0.1:6000 full
-~~~
-
-## Dnstap command line tool
-
-~~~ sh
-go get github.com/dnstap/golang-dnstap
-cd $GOPATH/src/github.com/dnstap/golang-dnstap/dnstap
-go build
-./dnstap
-~~~
-
-The following command listens on the given socket and decodes messages to stdout.
-
-~~~ sh
-dnstap -u /tmp/dnstap.sock
-~~~
-
-The following command listens on the given socket and saves message payloads to a binary dnstap-format log file.
-
-~~~ sh
-dnstap -u /tmp/dnstap.sock -w /tmp/test.dnstap
-~~~
-
-Listen for dnstap messages on port 6000.
-
-~~~ sh
-dnstap -l 127.0.0.1:6000
-~~~
diff --git a/middleware/dnstap/handler.go b/middleware/dnstap/handler.go
deleted file mode 100644
index eb2924be5..000000000
--- a/middleware/dnstap/handler.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package dnstap
-
-import (
- "fmt"
- "io"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/dnstap/msg"
- "github.com/coredns/coredns/middleware/dnstap/taprw"
-
- tap "github.com/dnstap/golang-dnstap"
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// Dnstap is the dnstap handler.
-type Dnstap struct {
- Next middleware.Handler
- Out io.Writer
- Pack bool
-}
-
-type (
- // Tapper is implemented by the Context passed by the dnstap handler.
- Tapper interface {
- TapMessage(*tap.Message) error
- TapBuilder() msg.Builder
- }
- tapContext struct {
- context.Context
- Dnstap
- }
-)
-
-// TapperFromContext will return a Tapper if the dnstap middleware is enabled.
-func TapperFromContext(ctx context.Context) (t Tapper) {
- t, _ = ctx.(Tapper)
- return
-}
-
-func tapMessageTo(w io.Writer, m *tap.Message) error {
- frame, err := msg.Marshal(m)
- if err != nil {
- return fmt.Errorf("marshal: %s", err)
- }
- _, err = w.Write(frame)
- return err
-}
-
-// TapMessage implements Tapper.
-func (h Dnstap) TapMessage(m *tap.Message) error {
- return tapMessageTo(h.Out, m)
-}
-
-// TapBuilder implements Tapper.
-func (h Dnstap) TapBuilder() msg.Builder {
- return msg.Builder{Full: h.Pack}
-}
-
-// ServeDNS logs the client query and response to dnstap and passes the dnstap Context.
-func (h Dnstap) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- rw := &taprw.ResponseWriter{ResponseWriter: w, Tapper: &h, Query: r}
- rw.QueryEpoch()
-
- code, err := middleware.NextOrFailure(h.Name(), h.Next, tapContext{ctx, h}, rw, r)
- if err != nil {
- // ignore dnstap errors
- return code, err
- }
-
- if err := rw.DnstapError(); err != nil {
- return code, middleware.Error("dnstap", err)
- }
-
- return code, nil
-}
-
-// Name returns dnstap.
-func (h Dnstap) Name() string { return "dnstap" }
diff --git a/middleware/dnstap/handler_test.go b/middleware/dnstap/handler_test.go
deleted file mode 100644
index dfdde582d..000000000
--- a/middleware/dnstap/handler_test.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package dnstap
-
-import (
- "errors"
- "fmt"
- "testing"
-
- "github.com/coredns/coredns/middleware/dnstap/test"
- mwtest "github.com/coredns/coredns/middleware/test"
-
- tap "github.com/dnstap/golang-dnstap"
- "github.com/golang/protobuf/proto"
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func testCase(t *testing.T, tapq, tapr *tap.Message, q, r *dns.Msg) {
- w := writer{}
- w.queue = append(w.queue, tapq, tapr)
- h := Dnstap{
- Next: mwtest.HandlerFunc(func(_ context.Context,
- w dns.ResponseWriter, _ *dns.Msg) (int, error) {
-
- return 0, w.WriteMsg(r)
- }),
- Out: &w,
- Pack: false,
- }
- _, err := h.ServeDNS(context.TODO(), &mwtest.ResponseWriter{}, q)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-type writer struct {
- queue []*tap.Message
-}
-
-func (w *writer) Write(b []byte) (int, error) {
- e := tap.Dnstap{}
- if err := proto.Unmarshal(b, &e); err != nil {
- return 0, err
- }
- if len(w.queue) == 0 {
- return 0, errors.New("message not expected")
- }
- if !test.MsgEqual(w.queue[0], e.Message) {
- return 0, fmt.Errorf("want: %v, have: %v", w.queue[0], e.Message)
- }
- w.queue = w.queue[1:]
- return len(b), nil
-}
-
-func TestDnstap(t *testing.T) {
- q := mwtest.Case{Qname: "example.org", Qtype: dns.TypeA}.Msg()
- r := mwtest.Case{
- Qname: "example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- mwtest.A("example.org. 3600 IN A 10.0.0.1"),
- },
- }.Msg()
- tapq := test.TestingData().ToClientQuery()
- tapr := test.TestingData().ToClientResponse()
- testCase(t, tapq, tapr, q, r)
-}
diff --git a/middleware/dnstap/msg/msg.go b/middleware/dnstap/msg/msg.go
deleted file mode 100644
index 0e2cd5d40..000000000
--- a/middleware/dnstap/msg/msg.go
+++ /dev/null
@@ -1,168 +0,0 @@
-// Package msg helps to build a dnstap Message.
-package msg
-
-import (
- "errors"
- "net"
- "strconv"
- "time"
-
- tap "github.com/dnstap/golang-dnstap"
- "github.com/miekg/dns"
-)
-
-// Builder helps to build Data by being aware of the dnstap middleware configuration.
-type Builder struct {
- Full bool
- Data
-}
-
-// AddrMsg parses the info of net.Addr and dns.Msg.
-func (b *Builder) AddrMsg(a net.Addr, m *dns.Msg) (err error) {
- err = b.RemoteAddr(a)
- if err != nil {
- return
- }
- return b.Msg(m)
-}
-
-// Msg parses the info of dns.Msg.
-func (b *Builder) Msg(m *dns.Msg) (err error) {
- if b.Full {
- err = b.Pack(m)
- }
- return
-}
-
-// Data helps to build a dnstap Message.
-// It can be transformed into the actual Message using this package.
-type Data struct {
- Packed []byte
- SocketProto tap.SocketProtocol
- SocketFam tap.SocketFamily
- Address []byte
- Port uint32
- TimeSec uint64
-}
-
-// HostPort decodes into Data any string returned by dnsutil.ParseHostPortOrFile.
-func (d *Data) HostPort(addr string) error {
- ip, port, err := net.SplitHostPort(addr)
- if err != nil {
- return err
- }
- p, err := strconv.ParseUint(port, 10, 32)
- if err != nil {
- return err
- }
- d.Port = uint32(p)
-
- if ip := net.ParseIP(ip); ip != nil {
- d.Address = []byte(ip)
- if ip := ip.To4(); ip != nil {
- d.SocketFam = tap.SocketFamily_INET
- } else {
- d.SocketFam = tap.SocketFamily_INET6
- }
- return nil
- }
- return errors.New("not an ip address")
-}
-
-// RemoteAddr parses the information about the remote address into Data.
-func (d *Data) RemoteAddr(remote net.Addr) error {
- switch addr := remote.(type) {
- case *net.TCPAddr:
- d.Address = addr.IP
- d.Port = uint32(addr.Port)
- d.SocketProto = tap.SocketProtocol_TCP
- case *net.UDPAddr:
- d.Address = addr.IP
- d.Port = uint32(addr.Port)
- d.SocketProto = tap.SocketProtocol_UDP
- default:
- return errors.New("unknown remote address type")
- }
-
- if a := net.IP(d.Address); a.To4() != nil {
- d.SocketFam = tap.SocketFamily_INET
- } else {
- d.SocketFam = tap.SocketFamily_INET6
- }
-
- return nil
-}
-
-// Pack encodes the DNS message into Data.
-func (d *Data) Pack(m *dns.Msg) error {
- packed, err := m.Pack()
- if err != nil {
- return err
- }
- d.Packed = packed
- return nil
-}
-
-// Epoch returns the epoch time in seconds.
-func Epoch() uint64 {
- return uint64(time.Now().Unix())
-}
-
-// Epoch sets the dnstap message epoch.
-func (d *Data) Epoch() {
- d.TimeSec = Epoch()
-}
-
-// ToClientResponse transforms Data into a client response message.
-func (d *Data) ToClientResponse() *tap.Message {
- t := tap.Message_CLIENT_RESPONSE
- return &tap.Message{
- Type: &t,
- SocketFamily: &d.SocketFam,
- SocketProtocol: &d.SocketProto,
- ResponseTimeSec: &d.TimeSec,
- ResponseMessage: d.Packed,
- QueryAddress: d.Address,
- QueryPort: &d.Port,
- }
-}
-
-// ToClientQuery transforms Data into a client query message.
-func (d *Data) ToClientQuery() *tap.Message {
- t := tap.Message_CLIENT_QUERY
- return &tap.Message{
- Type: &t,
- SocketFamily: &d.SocketFam,
- SocketProtocol: &d.SocketProto,
- QueryTimeSec: &d.TimeSec,
- QueryMessage: d.Packed,
- QueryAddress: d.Address,
- QueryPort: &d.Port,
- }
-}
-
-// ToOutsideQuery transforms the data into a forwarder or resolver query message.
-func (d *Data) ToOutsideQuery(t tap.Message_Type) *tap.Message {
- return &tap.Message{
- Type: &t,
- SocketFamily: &d.SocketFam,
- SocketProtocol: &d.SocketProto,
- QueryTimeSec: &d.TimeSec,
- QueryMessage: d.Packed,
- ResponseAddress: d.Address,
- ResponsePort: &d.Port,
- }
-}
-
-// ToOutsideResponse transforms the data into a forwarder or resolver response message.
-func (d *Data) ToOutsideResponse(t tap.Message_Type) *tap.Message {
- return &tap.Message{
- Type: &t,
- SocketFamily: &d.SocketFam,
- SocketProtocol: &d.SocketProto,
- ResponseTimeSec: &d.TimeSec,
- ResponseMessage: d.Packed,
- ResponseAddress: d.Address,
- ResponsePort: &d.Port,
- }
-}
diff --git a/middleware/dnstap/msg/msg_test.go b/middleware/dnstap/msg/msg_test.go
deleted file mode 100644
index 2f80a90cd..000000000
--- a/middleware/dnstap/msg/msg_test.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package msg
-
-import (
- "net"
- "reflect"
- "testing"
-
- "github.com/coredns/coredns/middleware/test"
- "github.com/coredns/coredns/request"
-
- tap "github.com/dnstap/golang-dnstap"
- "github.com/miekg/dns"
-)
-
-func testRequest(t *testing.T, expected Data, r request.Request) {
- d := Data{}
- if err := d.RemoteAddr(r.W.RemoteAddr()); err != nil {
- t.Fail()
- return
- }
- if d.SocketProto != expected.SocketProto ||
- d.SocketFam != expected.SocketFam ||
- !reflect.DeepEqual(d.Address, expected.Address) ||
- d.Port != expected.Port {
- t.Fatalf("expected: %v, have: %v", expected, d)
- return
- }
-}
-func TestRequest(t *testing.T) {
- testRequest(t, Data{
- SocketProto: tap.SocketProtocol_UDP,
- SocketFam: tap.SocketFamily_INET,
- Address: net.ParseIP("10.240.0.1"),
- Port: 40212,
- }, testingRequest())
-}
-func testingRequest() request.Request {
- m := new(dns.Msg)
- m.SetQuestion("example.com.", dns.TypeA)
- m.SetEdns0(4097, true)
- return request.Request{W: &test.ResponseWriter{}, Req: m}
-}
diff --git a/middleware/dnstap/msg/wrapper.go b/middleware/dnstap/msg/wrapper.go
deleted file mode 100644
index a74c604d8..000000000
--- a/middleware/dnstap/msg/wrapper.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package msg
-
-import (
- "fmt"
-
- lib "github.com/dnstap/golang-dnstap"
- "github.com/golang/protobuf/proto"
-)
-
-func wrap(m *lib.Message) *lib.Dnstap {
- t := lib.Dnstap_MESSAGE
- return &lib.Dnstap{
- Type: &t,
- Message: m,
- }
-}
-
-// Marshal encodes the message to a binary dnstap payload.
-func Marshal(m *lib.Message) (data []byte, err error) {
- data, err = proto.Marshal(wrap(m))
- if err != nil {
- err = fmt.Errorf("proto: %s", err)
- return
- }
- return
-}
diff --git a/middleware/dnstap/out/socket.go b/middleware/dnstap/out/socket.go
deleted file mode 100644
index 520dcf1d8..000000000
--- a/middleware/dnstap/out/socket.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package out
-
-import (
- "fmt"
- "net"
-
- fs "github.com/farsightsec/golang-framestream"
-)
-
-// Socket is a Frame Streams encoder over a UNIX socket.
-type Socket struct {
- path string
- enc *fs.Encoder
- conn net.Conn
- err error
-}
-
-func openSocket(s *Socket) error {
- conn, err := net.Dial("unix", s.path)
- if err != nil {
- return err
- }
- s.conn = conn
-
- enc, err := fs.NewEncoder(conn, &fs.EncoderOptions{
- ContentType: []byte("protobuf:dnstap.Dnstap"),
- Bidirectional: true,
- })
- if err != nil {
- return err
- }
- s.enc = enc
-
- s.err = nil
- return nil
-}
-
-// NewSocket will always return a new Socket.
-// err if nothing is listening to it, it will attempt to reconnect on the next Write.
-func NewSocket(path string) (s *Socket, err error) {
- s = &Socket{path: path}
- if err = openSocket(s); err != nil {
- err = fmt.Errorf("open socket: %s", err)
- s.err = err
- return
- }
- return
-}
-
-// Write a single Frame Streams frame.
-func (s *Socket) Write(frame []byte) (int, error) {
- if s.err != nil {
- // is the dnstap tool listening?
- if err := openSocket(s); err != nil {
- return 0, fmt.Errorf("open socket: %s", err)
- }
- }
- n, err := s.enc.Write(frame)
- if err != nil {
- // the dnstap command line tool is down
- s.conn.Close()
- s.err = err
- return 0, err
- }
- return n, nil
-
-}
-
-// Close the socket and flush the remaining frames.
-func (s *Socket) Close() error {
- if s.err != nil {
- // nothing to close
- return nil
- }
-
- defer s.conn.Close()
-
- if err := s.enc.Flush(); err != nil {
- return fmt.Errorf("flush: %s", err)
- }
- if err := s.enc.Close(); err != nil {
- return err
- }
-
- return nil
-}
diff --git a/middleware/dnstap/out/socket_test.go b/middleware/dnstap/out/socket_test.go
deleted file mode 100644
index 050a38d36..000000000
--- a/middleware/dnstap/out/socket_test.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package out
-
-import (
- "net"
- "testing"
-
- fs "github.com/farsightsec/golang-framestream"
-)
-
-func acceptOne(t *testing.T, l net.Listener) {
- server, err := l.Accept()
- if err != nil {
- t.Fatalf("server accept: %s", err)
- return
- }
-
- dec, err := fs.NewDecoder(server, &fs.DecoderOptions{
- ContentType: []byte("protobuf:dnstap.Dnstap"),
- Bidirectional: true,
- })
- if err != nil {
- t.Fatalf("server decoder: %s", err)
- return
- }
-
- if _, err := dec.Decode(); err != nil {
- t.Errorf("server decode: %s", err)
- }
-
- if err := server.Close(); err != nil {
- t.Error(err)
- }
-}
-func sendOne(socket *Socket) error {
- if _, err := socket.Write([]byte("frame")); err != nil {
- return err
- }
- if err := socket.enc.Flush(); err != nil {
- // Would happen during Write in real life.
- socket.conn.Close()
- socket.err = err
- return err
- }
- return nil
-}
-func TestSocket(t *testing.T) {
- socket, err := NewSocket("dnstap.sock")
- if err == nil {
- t.Fatal("new socket: not listening but no error")
- return
- }
-
- if err := sendOne(socket); err == nil {
- t.Fatal("not listening but no error")
- return
- }
-
- l, err := net.Listen("unix", "dnstap.sock")
- if err != nil {
- t.Fatal(err)
- return
- }
-
- wait := make(chan bool)
- go func() {
- acceptOne(t, l)
- wait <- true
- }()
-
- if err := sendOne(socket); err != nil {
- t.Fatalf("send one: %s", err)
- return
- }
-
- <-wait
- if err := sendOne(socket); err == nil {
- panic("must fail")
- }
-
- go func() {
- acceptOne(t, l)
- wait <- true
- }()
-
- if err := sendOne(socket); err != nil {
- t.Fatalf("send one: %s", err)
- return
- }
-
- <-wait
- if err := l.Close(); err != nil {
- t.Error(err)
- }
-}
diff --git a/middleware/dnstap/out/tcp.go b/middleware/dnstap/out/tcp.go
deleted file mode 100644
index 8d2c25270..000000000
--- a/middleware/dnstap/out/tcp.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package out
-
-import (
- "net"
- "time"
-
- fs "github.com/farsightsec/golang-framestream"
-)
-
-// TCP is a Frame Streams encoder over TCP.
-type TCP struct {
- address string
- frames [][]byte
-}
-
-// NewTCP returns a TCP writer.
-func NewTCP(address string) *TCP {
- s := &TCP{address: address}
- s.frames = make([][]byte, 0, 13) // 13 messages buffer
- return s
-}
-
-// Write a single Frame Streams frame.
-func (s *TCP) Write(frame []byte) (n int, err error) {
- s.frames = append(s.frames, frame)
- if len(s.frames) == cap(s.frames) {
- return len(frame), s.Flush()
- }
- return len(frame), nil
-}
-
-// Flush the remaining frames.
-func (s *TCP) Flush() error {
- defer func() {
- s.frames = s.frames[0:]
- }()
- c, err := net.DialTimeout("tcp", s.address, time.Second)
- if err != nil {
- return err
- }
- enc, err := fs.NewEncoder(c, &fs.EncoderOptions{
- ContentType: []byte("protobuf:dnstap.Dnstap"),
- Bidirectional: true,
- })
- if err != nil {
- return err
- }
- for _, frame := range s.frames {
- if _, err = enc.Write(frame); err != nil {
- return err
- }
- }
- return enc.Flush()
-}
-
-// Close is an alias to Flush to satisfy io.WriteCloser similarly to type Socket.
-func (s *TCP) Close() error {
- return s.Flush()
-}
diff --git a/middleware/dnstap/out/tcp_test.go b/middleware/dnstap/out/tcp_test.go
deleted file mode 100644
index 113603cd4..000000000
--- a/middleware/dnstap/out/tcp_test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package out
-
-import (
- "net"
- "testing"
-)
-
-func sendOneTCP(tcp *TCP) error {
- if _, err := tcp.Write([]byte("frame")); err != nil {
- return err
- }
- if err := tcp.Flush(); err != nil {
- return err
- }
- return nil
-}
-func TestTCP(t *testing.T) {
- tcp := NewTCP("localhost:14000")
-
- if err := sendOneTCP(tcp); err == nil {
- t.Fatal("Not listening but no error.")
- return
- }
-
- l, err := net.Listen("tcp", "localhost:14000")
- if err != nil {
- t.Fatal(err)
- return
- }
-
- wait := make(chan bool)
- go func() {
- acceptOne(t, l)
- wait <- true
- }()
-
- if err := sendOneTCP(tcp); err != nil {
- t.Fatalf("send one: %s", err)
- return
- }
-
- <-wait
-
- // TODO: When the server isn't responding according to the framestream protocol
- // the thread is blocked.
- /*
- if err := sendOneTCP(tcp); err == nil {
- panic("must fail")
- }
- */
-
- go func() {
- acceptOne(t, l)
- wait <- true
- }()
-
- if err := sendOneTCP(tcp); err != nil {
- t.Fatalf("send one: %s", err)
- return
- }
-
- <-wait
- if err := l.Close(); err != nil {
- t.Error(err)
- }
-}
diff --git a/middleware/dnstap/setup.go b/middleware/dnstap/setup.go
deleted file mode 100644
index 63a3eb099..000000000
--- a/middleware/dnstap/setup.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package dnstap
-
-import (
- "fmt"
- "io"
- "log"
- "strings"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/dnstap/out"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
-
- "github.com/mholt/caddy"
- "github.com/mholt/caddy/caddyfile"
-)
-
-func init() {
- caddy.RegisterPlugin("dnstap", caddy.Plugin{
- ServerType: "dns",
- Action: wrapSetup,
- })
-}
-
-func wrapSetup(c *caddy.Controller) error {
- if err := setup(c); err != nil {
- return middleware.Error("dnstap", err)
- }
- return nil
-}
-
-type config struct {
- target string
- socket bool
- full bool
-}
-
-func parseConfig(d *caddyfile.Dispenser) (c config, err error) {
- d.Next() // directive name
-
- if !d.Args(&c.target) {
- return c, d.ArgErr()
- }
-
- if strings.HasPrefix(c.target, "tcp://") {
- // remote IP endpoint
- servers, err := dnsutil.ParseHostPortOrFile(c.target[6:])
- if err != nil {
- return c, d.ArgErr()
- }
- c.target = servers[0]
- } else {
- // default to UNIX socket
- if strings.HasPrefix(c.target, "unix://") {
- c.target = c.target[7:]
- }
- c.socket = true
- }
-
- c.full = d.NextArg() && d.Val() == "full"
-
- return
-}
-
-func setup(c *caddy.Controller) error {
- conf, err := parseConfig(&c.Dispenser)
- if err != nil {
- return err
- }
-
- dnstap := Dnstap{Pack: conf.full}
-
- var o io.WriteCloser
- if conf.socket {
- o, err = out.NewSocket(conf.target)
- if err != nil {
- log.Printf("[WARN] Can't connect to %s at the moment: %s", conf.target, err)
- }
- } else {
- o = out.NewTCP(conf.target)
- }
- dnstap.Out = o
-
- c.OnShutdown(func() error {
- if err := o.Close(); err != nil {
- return fmt.Errorf("output: %s", err)
- }
- return nil
- })
-
- dnsserver.GetConfig(c).AddMiddleware(
- func(next middleware.Handler) middleware.Handler {
- dnstap.Next = next
- return dnstap
- })
-
- return nil
-}
diff --git a/middleware/dnstap/setup_test.go b/middleware/dnstap/setup_test.go
deleted file mode 100644
index fc1dc98e0..000000000
--- a/middleware/dnstap/setup_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package dnstap
-
-import (
- "github.com/mholt/caddy"
- "testing"
-)
-
-func TestConfig(t *testing.T) {
- tests := []struct {
- file string
- path string
- full bool
- socket bool
- fail bool
- }{
- {"dnstap dnstap.sock full", "dnstap.sock", true, true, false},
- {"dnstap unix://dnstap.sock", "dnstap.sock", false, true, false},
- {"dnstap tcp://127.0.0.1:6000", "127.0.0.1:6000", false, false, false},
- {"dnstap", "fail", false, true, true},
- }
- for _, c := range tests {
- cad := caddy.NewTestController("dns", c.file)
- conf, err := parseConfig(&cad.Dispenser)
- if c.fail {
- if err == nil {
- t.Errorf("%s: %s", c.file, err)
- }
- } else if err != nil || conf.target != c.path ||
- conf.full != c.full || conf.socket != c.socket {
-
- t.Errorf("expected: %+v\nhave: %+v\nerror: %s\n", c, conf, err)
- }
- }
-}
diff --git a/middleware/dnstap/taprw/writer.go b/middleware/dnstap/taprw/writer.go
deleted file mode 100644
index 99572afd9..000000000
--- a/middleware/dnstap/taprw/writer.go
+++ /dev/null
@@ -1,73 +0,0 @@
-// Package taprw takes a query and intercepts the response.
-// It will log both after the response is written.
-package taprw
-
-import (
- "fmt"
-
- "github.com/coredns/coredns/middleware/dnstap/msg"
-
- tap "github.com/dnstap/golang-dnstap"
- "github.com/miekg/dns"
-)
-
-// Tapper is what ResponseWriter needs to log to dnstap.
-type Tapper interface {
- TapMessage(m *tap.Message) error
- TapBuilder() msg.Builder
-}
-
-// ResponseWriter captures the client response and logs the query to dnstap.
-// Single request use.
-type ResponseWriter struct {
- queryEpoch uint64
- Query *dns.Msg
- dns.ResponseWriter
- Tapper
- err error
-}
-
-// DnstapError check if a dnstap error occurred during Write and returns it.
-func (w ResponseWriter) DnstapError() error {
- return w.err
-}
-
-// QueryEpoch sets the query epoch as reported by dnstap.
-func (w *ResponseWriter) QueryEpoch() {
- w.queryEpoch = msg.Epoch()
-}
-
-// WriteMsg writes back the response to the client and THEN works on logging the request
-// and response to dnstap.
-// Dnstap errors are to be checked by DnstapError.
-func (w *ResponseWriter) WriteMsg(resp *dns.Msg) (writeErr error) {
- writeErr = w.ResponseWriter.WriteMsg(resp)
- writeEpoch := msg.Epoch()
-
- b := w.TapBuilder()
- b.TimeSec = w.queryEpoch
- if err := func() (err error) {
- err = b.AddrMsg(w.ResponseWriter.RemoteAddr(), w.Query)
- if err != nil {
- return
- }
- return w.TapMessage(b.ToClientQuery())
- }(); err != nil {
- w.err = fmt.Errorf("client query: %s", err)
- // don't forget to call DnstapError later
- }
-
- if writeErr == nil {
- if err := func() (err error) {
- b.TimeSec = writeEpoch
- if err = b.Msg(resp); err != nil {
- return
- }
- return w.TapMessage(b.ToClientResponse())
- }(); err != nil {
- w.err = fmt.Errorf("client response: %s", err)
- }
- }
-
- return
-}
diff --git a/middleware/dnstap/taprw/writer_test.go b/middleware/dnstap/taprw/writer_test.go
deleted file mode 100644
index 426f1f580..000000000
--- a/middleware/dnstap/taprw/writer_test.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package taprw
-
-import (
- "errors"
- "testing"
-
- "github.com/coredns/coredns/middleware/dnstap/msg"
- "github.com/coredns/coredns/middleware/dnstap/test"
- mwtest "github.com/coredns/coredns/middleware/test"
-
- tap "github.com/dnstap/golang-dnstap"
- "github.com/miekg/dns"
-)
-
-type TapFailer struct {
-}
-
-func (TapFailer) TapMessage(*tap.Message) error {
- return errors.New("failed")
-}
-func (TapFailer) TapBuilder() msg.Builder {
- return msg.Builder{Full: true}
-}
-
-func TestDnstapError(t *testing.T) {
- rw := ResponseWriter{
- Query: new(dns.Msg),
- ResponseWriter: &mwtest.ResponseWriter{},
- Tapper: TapFailer{},
- }
- if err := rw.WriteMsg(new(dns.Msg)); err != nil {
- t.Errorf("dnstap error during Write: %s", err)
- }
- if rw.DnstapError() == nil {
- t.Fatal("no dnstap error")
- }
-}
-
-func testingMsg() (m *dns.Msg) {
- m = new(dns.Msg)
- m.SetQuestion("example.com.", dns.TypeA)
- m.SetEdns0(4097, true)
- return
-}
-
-func TestClientQueryResponse(t *testing.T) {
- trapper := test.TrapTapper{Full: true}
- m := testingMsg()
- rw := ResponseWriter{
- Query: m,
- Tapper: &trapper,
- ResponseWriter: &mwtest.ResponseWriter{},
- }
- d := test.TestingData()
-
- // will the wire-format msg be reported?
- bin, err := m.Pack()
- if err != nil {
- t.Fatal(err)
- return
- }
- d.Packed = bin
-
- if err := rw.WriteMsg(m); err != nil {
- t.Fatal(err)
- return
- }
- if l := len(trapper.Trap); l != 2 {
- t.Fatalf("%d msg trapped", l)
- return
- }
- want := d.ToClientQuery()
- have := trapper.Trap[0]
- if !test.MsgEqual(want, have) {
- t.Fatalf("query: want: %v\nhave: %v", want, have)
- }
- want = d.ToClientResponse()
- have = trapper.Trap[1]
- if !test.MsgEqual(want, have) {
- t.Fatalf("response: want: %v\nhave: %v", want, have)
- }
-}
diff --git a/middleware/dnstap/test/helpers.go b/middleware/dnstap/test/helpers.go
deleted file mode 100644
index 46ba327ab..000000000
--- a/middleware/dnstap/test/helpers.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package test
-
-import (
- "net"
- "reflect"
-
- "github.com/coredns/coredns/middleware/dnstap/msg"
-
- tap "github.com/dnstap/golang-dnstap"
- "golang.org/x/net/context"
-)
-
-// Context is a message trap.
-type Context struct {
- context.Context
- TrapTapper
-}
-
-// TestingData returns the Data matching coredns/test.ResponseWriter.
-func TestingData() (d *msg.Data) {
- d = &msg.Data{
- SocketFam: tap.SocketFamily_INET,
- SocketProto: tap.SocketProtocol_UDP,
- Address: net.ParseIP("10.240.0.1"),
- Port: 40212,
- }
- return
-}
-
-type comp struct {
- Type *tap.Message_Type
- SF *tap.SocketFamily
- SP *tap.SocketProtocol
- QA []byte
- RA []byte
- QP *uint32
- RP *uint32
- QTSec bool
- RTSec bool
- RM []byte
- QM []byte
-}
-
-func toComp(m *tap.Message) comp {
- return comp{
- Type: m.Type,
- SF: m.SocketFamily,
- SP: m.SocketProtocol,
- QA: m.QueryAddress,
- RA: m.ResponseAddress,
- QP: m.QueryPort,
- RP: m.ResponsePort,
- QTSec: m.QueryTimeSec != nil,
- RTSec: m.ResponseTimeSec != nil,
- RM: m.ResponseMessage,
- QM: m.QueryMessage,
- }
-}
-
-// MsgEqual compares two dnstap messages ignoring timestamps.
-func MsgEqual(a, b *tap.Message) bool {
- return reflect.DeepEqual(toComp(a), toComp(b))
-}
-
-// TrapTapper traps messages.
-type TrapTapper struct {
- Trap []*tap.Message
- Full bool
-}
-
-// TapMessage adds the message to the trap.
-func (t *TrapTapper) TapMessage(m *tap.Message) error {
- t.Trap = append(t.Trap, m)
- return nil
-}
-
-// TapBuilder returns a test msg.Builder.
-func (t *TrapTapper) TapBuilder() msg.Builder {
- return msg.Builder{Full: t.Full}
-}
diff --git a/middleware/erratic/README.md b/middleware/erratic/README.md
deleted file mode 100644
index a84146fbb..000000000
--- a/middleware/erratic/README.md
+++ /dev/null
@@ -1,76 +0,0 @@
-# erratic
-
-*erratic* is a middleware useful for testing client behavior. It returns a static response to all
-queries, but the responses can be delayed, dropped or truncated.
-
-The *erratic* middleware will respond to every A or AAAA query. For any other type it will return
-a SERVFAIL response. The reply for A will return 192.0.2.53 (see RFC 5737), for AAAA it returns
-2001:DB8::53 (see RFC 3849).
-
-*erratic* can also be used in conjunction with the *autopath* middleware. This is mostly to aid in
- testing.
-
-## Syntax
-
-~~~ txt
-erratic {
- drop [AMOUNT]
- truncate [AMOUNT]
- delay [AMOUNT [DURATION]]
-}
-~~~
-
-* `drop`: drop 1 per **AMOUNT** of queries, the default is 2.
-* `truncate`: truncate 1 per **AMOUNT** of queries, the default is 2.
-* `delay`: delay 1 per **AMOUNT** of queries for **DURATION**, the default for **AMOUNT** is 2 and
- the default for **DURATION** is 100ms.
-
-## Examples
-
-~~~ txt
-.:53 {
- erratic {
- drop 3
- }
-}
-~~~
-
-Or even shorter if the defaults suits you. Note this only drops queries, it does not delay them.
-
-~~~ txt
-. {
- erratic
-}
-~~~
-
-Delay 1 in 3 queries for 50ms
-
-~~~ txt
-. {
- erratic {
- delay 3 50ms
- }
-}
-~~~
-
-Delay 1 in 3 and truncate 1 in 5.
-
-~~~ txt
-. {
- erratic {
- delay 3 5ms
- truncate 5
- }
-}
-~~~
-
-Drop every second query.
-
-~~~ txt
-. {
- erratic {
- drop 2
- truncate 2
- }
-}
-~~~
diff --git a/middleware/erratic/autopath.go b/middleware/erratic/autopath.go
deleted file mode 100644
index 79b0ec847..000000000
--- a/middleware/erratic/autopath.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package erratic
-
-import "github.com/coredns/coredns/request"
-
-// AutoPath implements the AutoPathFunc call from the autopath middleware.
-func (e *Erratic) AutoPath(state request.Request) []string {
- return []string{"a.example.org.", "b.example.org.", ""}
-}
diff --git a/middleware/erratic/erratic.go b/middleware/erratic/erratic.go
deleted file mode 100644
index b05e45f03..000000000
--- a/middleware/erratic/erratic.go
+++ /dev/null
@@ -1,95 +0,0 @@
-// Package erratic implements a middleware that returns erratic answers (delayed, dropped).
-package erratic
-
-import (
- "sync/atomic"
- "time"
-
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// Erratic is a middleware that returns erratic repsonses to each client.
-type Erratic struct {
- drop uint64
-
- delay uint64
- duration time.Duration
-
- truncate uint64
-
- q uint64 // counter of queries
-}
-
-// ServeDNS implements the middleware.Handler interface.
-func (e *Erratic) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- state := request.Request{W: w, Req: r}
- drop := false
- delay := false
- trunc := false
-
- queryNr := atomic.LoadUint64(&e.q)
- atomic.AddUint64(&e.q, 1)
-
- if e.drop > 0 && queryNr%e.drop == 0 {
- drop = true
- }
- if e.delay > 0 && queryNr%e.delay == 0 {
- delay = true
- }
- if e.truncate > 0 && queryNr&e.truncate == 0 {
- trunc = true
- }
-
- m := new(dns.Msg)
- m.SetReply(r)
- m.Compress = true
- m.Authoritative = true
- if trunc {
- m.Truncated = true
- }
-
- // small dance to copy rrA or rrAAAA into a non-pointer var that allows us to overwrite the ownername
- // in a non-racy way.
- switch state.QType() {
- case dns.TypeA:
- rr := *(rrA.(*dns.A))
- rr.Header().Name = state.QName()
- m.Answer = append(m.Answer, &rr)
- case dns.TypeAAAA:
- rr := *(rrAAAA.(*dns.AAAA))
- rr.Header().Name = state.QName()
- m.Answer = append(m.Answer, &rr)
- default:
- if !drop {
- if delay {
- time.Sleep(e.duration)
- }
- // coredns will return error.
- return dns.RcodeServerFailure, nil
- }
- }
-
- if drop {
- return 0, nil
- }
-
- if delay {
- time.Sleep(e.duration)
- }
-
- state.SizeAndDo(m)
- w.WriteMsg(m)
-
- return 0, nil
-}
-
-// Name implements the Handler interface.
-func (e *Erratic) Name() string { return "erratic" }
-
-var (
- rrA, _ = dns.NewRR(". IN 0 A 192.0.2.53")
- rrAAAA, _ = dns.NewRR(". IN 0 AAAA 2001:DB8::53")
-)
diff --git a/middleware/erratic/erratic_test.go b/middleware/erratic/erratic_test.go
deleted file mode 100644
index 4b54e0c12..000000000
--- a/middleware/erratic/erratic_test.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package erratic
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func TestErraticDrop(t *testing.T) {
- e := &Erratic{drop: 2} // 50% drops
-
- tests := []struct {
- expectedCode int
- expectedErr error
- drop bool
- }{
- {expectedCode: dns.RcodeSuccess, expectedErr: nil, drop: true},
- {expectedCode: dns.RcodeSuccess, expectedErr: nil, drop: false},
- }
-
- ctx := context.TODO()
-
- for i, tc := range tests {
- req := new(dns.Msg)
- req.SetQuestion("example.org.", dns.TypeA)
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- code, err := e.ServeDNS(ctx, rec, req)
-
- if err != tc.expectedErr {
- t.Errorf("Test %d: Expected error %q, but got %q", i, tc.expectedErr, err)
- }
- if code != int(tc.expectedCode) {
- t.Errorf("Test %d: Expected status code %d, but got %d", i, tc.expectedCode, code)
- }
-
- if tc.drop && rec.Msg != nil {
- t.Errorf("Test %d: Expected dropped message, but got %q", i, rec.Msg.Question[0].Name)
- }
- }
-}
-
-func TestErraticTruncate(t *testing.T) {
- e := &Erratic{truncate: 2} // 50% drops
-
- tests := []struct {
- expectedCode int
- expectedErr error
- truncate bool
- }{
- {expectedCode: dns.RcodeSuccess, expectedErr: nil, truncate: true},
- {expectedCode: dns.RcodeSuccess, expectedErr: nil, truncate: false},
- }
-
- ctx := context.TODO()
-
- for i, tc := range tests {
- req := new(dns.Msg)
- req.SetQuestion("example.org.", dns.TypeA)
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- code, err := e.ServeDNS(ctx, rec, req)
-
- if err != tc.expectedErr {
- t.Errorf("Test %d: Expected error %q, but got %q", i, tc.expectedErr, err)
- }
- if code != int(tc.expectedCode) {
- t.Errorf("Test %d: Expected status code %d, but got %d", i, tc.expectedCode, code)
- }
-
- if tc.truncate && !rec.Msg.Truncated {
- t.Errorf("Test %d: Expected truncated message, but got %q", i, rec.Msg.Question[0].Name)
- }
- }
-}
diff --git a/middleware/erratic/setup.go b/middleware/erratic/setup.go
deleted file mode 100644
index 98db02247..000000000
--- a/middleware/erratic/setup.go
+++ /dev/null
@@ -1,117 +0,0 @@
-package erratic
-
-import (
- "fmt"
- "strconv"
- "time"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("erratic", caddy.Plugin{
- ServerType: "dns",
- Action: setupErratic,
- })
-}
-
-func setupErratic(c *caddy.Controller) error {
- e, err := parseErratic(c)
- if err != nil {
- return middleware.Error("erratic", err)
- }
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- return e
- })
-
- return nil
-}
-
-func parseErratic(c *caddy.Controller) (*Erratic, error) {
- e := &Erratic{drop: 2}
- drop := false // true if we've seen the drop keyword
-
- for c.Next() { // 'erratic'
- for c.NextBlock() {
- switch c.Val() {
- case "drop":
- args := c.RemainingArgs()
- if len(args) > 1 {
- return nil, c.ArgErr()
- }
-
- if len(args) == 0 {
- continue
- }
-
- amount, err := strconv.ParseInt(args[0], 10, 32)
- if err != nil {
- return nil, err
- }
- if amount < 0 {
- return nil, fmt.Errorf("illegal amount value given %q", args[0])
- }
- e.drop = uint64(amount)
- drop = true
- case "delay":
- args := c.RemainingArgs()
- if len(args) > 2 {
- return nil, c.ArgErr()
- }
-
- // Defaults.
- e.delay = 2
- e.duration = 100 * time.Millisecond
- if len(args) == 0 {
- continue
- }
-
- amount, err := strconv.ParseInt(args[0], 10, 32)
- if err != nil {
- return nil, err
- }
- if amount < 0 {
- return nil, fmt.Errorf("illegal amount value given %q", args[0])
- }
- e.delay = uint64(amount)
-
- if len(args) > 1 {
- duration, err := time.ParseDuration(args[1])
- if err != nil {
- return nil, err
- }
- e.duration = duration
- }
- case "truncate":
- args := c.RemainingArgs()
- if len(args) > 1 {
- return nil, c.ArgErr()
- }
-
- if len(args) == 0 {
- continue
- }
-
- amount, err := strconv.ParseInt(args[0], 10, 32)
- if err != nil {
- return nil, err
- }
- if amount < 0 {
- return nil, fmt.Errorf("illegal amount value given %q", args[0])
- }
- e.truncate = uint64(amount)
- default:
- return nil, c.Errf("unknown property '%s'", c.Val())
- }
- }
- }
- if (e.delay > 0 || e.truncate > 0) && !drop { // delay is set, but we've haven't seen a drop keyword, remove default drop stuff
- e.drop = 0
- }
-
- return e, nil
-}
diff --git a/middleware/erratic/setup_test.go b/middleware/erratic/setup_test.go
deleted file mode 100644
index 759845f7a..000000000
--- a/middleware/erratic/setup_test.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package erratic
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestSetupErratic(t *testing.T) {
- c := caddy.NewTestController("dns", `erratic {
- drop
- }`)
- if err := setupErratic(c); err != nil {
- t.Fatalf("Test 1, expected no errors, but got: %q", err)
- }
-
- c = caddy.NewTestController("dns", `erratic`)
- if err := setupErratic(c); err != nil {
- t.Fatalf("Test 2, expected no errors, but got: %q", err)
- }
-
- c = caddy.NewTestController("dns", `erratic {
- drop -1
- }`)
- if err := setupErratic(c); err == nil {
- t.Fatalf("Test 4, expected errors, but got: %q", err)
- }
-}
-
-func TestParseErratic(t *testing.T) {
- tests := []struct {
- input string
- shouldErr bool
- drop uint64
- delay uint64
- truncate uint64
- }{
- // oks
- {`erratic`, false, 2, 0, 0},
- {`erratic {
- drop 2
- delay 3 1ms
-
- }`, false, 2, 3, 0},
- {`erratic {
- truncate 2
- delay 3 1ms
-
- }`, false, 0, 3, 2},
- {`erraric {
- drop 3
- delay
- }`, false, 3, 2, 0},
- // fails
- {`erratic {
- drop -1
- }`, true, 0, 0, 0},
- {`erratic {
- delay -1
- }`, true, 0, 0, 0},
- {`erratic {
- delay 1 2 4
- }`, true, 0, 0, 0},
- {`erratic {
- delay 15.a
- }`, true, 0, 0, 0},
- {`erraric {
- drop 3
- delay 3 bla
- }`, true, 0, 0, 0},
- {`erraric {
- truncate 15.a
- }`, true, 0, 0, 0},
- {`erraric {
- something-else
- }`, true, 0, 0, 0},
- }
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- e, err := parseErratic(c)
- if test.shouldErr && err == nil {
- t.Errorf("Test %v: Expected error but found nil", i)
- continue
- } else if !test.shouldErr && err != nil {
- t.Errorf("Test %v: Expected no error but found error: %v", i, err)
- continue
- }
-
- if test.shouldErr {
- continue
- }
-
- if test.delay != e.delay {
- t.Errorf("Test %v: Expected delay %d but found: %d", i, test.delay, e.delay)
- }
- if test.drop != e.drop {
- t.Errorf("Test %v: Expected drop %d but found: %d", i, test.drop, e.drop)
- }
- if test.truncate != e.truncate {
- t.Errorf("Test %v: Expected truncate %d but found: %d", i, test.truncate, e.truncate)
- }
- }
-}
diff --git a/middleware/errors/README.md b/middleware/errors/README.md
deleted file mode 100644
index 21b8f4848..000000000
--- a/middleware/errors/README.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# errors
-
-*errors* enables error logging.
-
-Any errors encountered during the query processing will be printed to standard output.
-
-## Syntax
-
-~~~
-errors
-~~~
-
-## Examples
-
-Use the *whoami* to respond to queries and Log errors to standard output.
-
-~~~ corefile
-. {
- whoami
- errors
-}
-~~~
diff --git a/middleware/errors/errors.go b/middleware/errors/errors.go
deleted file mode 100644
index 9b0bfa946..000000000
--- a/middleware/errors/errors.go
+++ /dev/null
@@ -1,79 +0,0 @@
-// Package errors implements an HTTP error handling middleware.
-package errors
-
-import (
- "fmt"
- "log"
- "runtime"
- "strings"
- "time"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// errorHandler handles DNS errors (and errors from other middleware).
-type errorHandler struct {
- Next middleware.Handler
- LogFile string
- Log *log.Logger
-}
-
-// ServeDNS implements the middleware.Handler interface.
-func (h errorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- defer h.recovery(ctx, w, r)
-
- rcode, err := middleware.NextOrFailure(h.Name(), h.Next, ctx, w, r)
-
- if err != nil {
- state := request.Request{W: w, Req: r}
- errMsg := fmt.Sprintf("%s [ERROR %d %s %s] %v", time.Now().Format(timeFormat), rcode, state.Name(), state.Type(), err)
-
- h.Log.Println(errMsg)
- }
-
- return rcode, err
-}
-
-func (h errorHandler) Name() string { return "errors" }
-
-func (h errorHandler) recovery(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) {
- rec := recover()
- if rec == nil {
- return
- }
-
- // Obtain source of panic
- // From: https://gist.github.com/swdunlop/9629168
- var name, file string // function name, file name
- var line int
- var pc [16]uintptr
- n := runtime.Callers(3, pc[:])
- for _, pc := range pc[:n] {
- fn := runtime.FuncForPC(pc)
- if fn == nil {
- continue
- }
- file, line = fn.FileLine(pc)
- name = fn.Name()
- if !strings.HasPrefix(name, "runtime.") {
- break
- }
- }
-
- // Trim file path
- delim := "/coredns/"
- pkgPathPos := strings.Index(file, delim)
- if pkgPathPos > -1 && len(file) > pkgPathPos+len(delim) {
- file = file[pkgPathPos+len(delim):]
- }
-
- panicMsg := fmt.Sprintf("%s [PANIC %s %s] %s:%d - %v", time.Now().Format(timeFormat), r.Question[0].Name, dns.Type(r.Question[0].Qtype), file, line, rec)
- // Currently we don't use the function name, since file:line is more conventional
- h.Log.Printf(panicMsg)
-}
-
-const timeFormat = "02/Jan/2006:15:04:05 -0700"
diff --git a/middleware/errors/errors_test.go b/middleware/errors/errors_test.go
deleted file mode 100644
index 5e565964c..000000000
--- a/middleware/errors/errors_test.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package errors
-
-import (
- "bytes"
- "errors"
- "fmt"
- "log"
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func TestErrors(t *testing.T) {
- buf := bytes.Buffer{}
- em := errorHandler{Log: log.New(&buf, "", 0)}
-
- testErr := errors.New("test error")
- tests := []struct {
- next middleware.Handler
- expectedCode int
- expectedLog string
- expectedErr error
- }{
- {
- next: genErrorHandler(dns.RcodeSuccess, nil),
- expectedCode: dns.RcodeSuccess,
- expectedLog: "",
- expectedErr: nil,
- },
- {
- next: genErrorHandler(dns.RcodeNotAuth, testErr),
- expectedCode: dns.RcodeNotAuth,
- expectedLog: fmt.Sprintf("[ERROR %d %s] %v\n", dns.RcodeNotAuth, "example.org. A", testErr),
- expectedErr: testErr,
- },
- }
-
- ctx := context.TODO()
- req := new(dns.Msg)
- req.SetQuestion("example.org.", dns.TypeA)
-
- for i, tc := range tests {
- em.Next = tc.next
- buf.Reset()
- rec := dnsrecorder.New(&test.ResponseWriter{})
- code, err := em.ServeDNS(ctx, rec, req)
-
- if err != tc.expectedErr {
- t.Errorf("Test %d: Expected error %v, but got %v",
- i, tc.expectedErr, err)
- }
- if code != tc.expectedCode {
- t.Errorf("Test %d: Expected status code %d, but got %d",
- i, tc.expectedCode, code)
- }
- if log := buf.String(); !strings.Contains(log, tc.expectedLog) {
- t.Errorf("Test %d: Expected log %q, but got %q",
- i, tc.expectedLog, log)
- }
- }
-}
-
-func genErrorHandler(rcode int, err error) middleware.Handler {
- return middleware.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- return rcode, err
- })
-}
diff --git a/middleware/errors/setup.go b/middleware/errors/setup.go
deleted file mode 100644
index af9a7bbff..000000000
--- a/middleware/errors/setup.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package errors
-
-import (
- "fmt"
- "log"
- "os"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("errors", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- handler, err := errorsParse(c)
- if err != nil {
- return middleware.Error("errors", err)
- }
-
- handler.Log = log.New(os.Stdout, "", 0)
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- handler.Next = next
- return handler
- })
-
- return nil
-}
-
-func errorsParse(c *caddy.Controller) (errorHandler, error) {
- handler := errorHandler{}
-
- for c.Next() {
- args := c.RemainingArgs()
- switch len(args) {
- case 0:
- handler.LogFile = "stdout"
- case 1:
- if args[0] != "stdout" {
- return handler, fmt.Errorf("invalid log file: %s", args[0])
- }
- handler.LogFile = args[0]
- default:
- return handler, c.ArgErr()
- }
- }
- return handler, nil
-}
diff --git a/middleware/errors/setup_test.go b/middleware/errors/setup_test.go
deleted file mode 100644
index bae85da32..000000000
--- a/middleware/errors/setup_test.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package errors
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestErrorsParse(t *testing.T) {
- tests := []struct {
- inputErrorsRules string
- shouldErr bool
- expectedErrorHandler errorHandler
- }{
- {`errors`, false, errorHandler{
- LogFile: "stdout",
- }},
- {`errors stdout`, false, errorHandler{
- LogFile: "stdout",
- }},
- {`errors errors.txt`, true, errorHandler{
- LogFile: "",
- }},
- {`errors visible`, true, errorHandler{
- LogFile: "",
- }},
- {`errors { log visible }`, true, errorHandler{
- LogFile: "stdout",
- }},
- }
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.inputErrorsRules)
- actualErrorsRule, err := errorsParse(c)
-
- if err == nil && test.shouldErr {
- t.Errorf("Test %d didn't error, but it should have", i)
- } else if err != nil && !test.shouldErr {
- t.Errorf("Test %d errored, but it shouldn't have; got '%v'", i, err)
- }
- if actualErrorsRule.LogFile != test.expectedErrorHandler.LogFile {
- t.Errorf("Test %d expected LogFile to be %s, but got %s",
- i, test.expectedErrorHandler.LogFile, actualErrorsRule.LogFile)
- }
- }
-}
diff --git a/middleware/etcd/README.md b/middleware/etcd/README.md
deleted file mode 100644
index f7991eb7c..000000000
--- a/middleware/etcd/README.md
+++ /dev/null
@@ -1,109 +0,0 @@
-# etcd
-
-*etcd* enables reading zone data from an etcd instance. The data in etcd has to be encoded as
-a [message](https://github.com/skynetservices/skydns/blob/2fcff74cdc9f9a7dd64189a447ef27ac354b725f/msg/service.go#L26)
-like [SkyDNS](https://github.com/skynetservices/skydns). It should also work just like SkyDNS.
-
-The etcd middleware makes extensive use of the proxy middleware to forward and query other servers
-in the network.
-
-## Syntax
-
-~~~
-etcd [ZONES...]
-~~~
-
-* **ZONES** zones etcd should be authoritative for.
-
-The path will default to `/skydns` the local etcd proxy (http://localhost:2379).
-If no zones are specified the block's zone will be used as the zone.
-
-If you want to `round robin` A and AAAA responses look at the `loadbalance` middleware.
-
-~~~
-etcd [ZONES...] {
- stubzones
- fallthrough
- path PATH
- endpoint ENDPOINT...
- upstream ADDRESS...
- tls CERT KEY CACERT
-}
-~~~
-
-* `stubzones` enables the stub zones feature. The stubzone is *only* done in the etcd tree located
- under the *first* zone specified.
-* `fallthrough` If zone matches but no record can be generated, pass request to the next middleware.
-* **PATH** the path inside etcd. Defaults to "/skydns".
-* **ENDPOINT** the etcd endpoints. Defaults to "http://localhost:2397".
-* `upstream` upstream resolvers to be used resolve external names found in etcd (think CNAMEs)
- pointing to external names. If you want CoreDNS to act as a proxy for clients, you'll need to add
- the proxy middleware. **ADDRESS** can be an IP address, and IP:port or a string pointing to a file
- that is structured as /etc/resolv.conf.
-* `tls` followed by:
- * no arguments, if the server certificate is signed by a system-installed CA and no client cert is needed
- * a single argument that is the CA PEM file, if the server cert is not signed by a system CA and no client cert is needed
- * two arguments - path to cert PEM file, the path to private key PEM file - if the server certificate is signed by a system-installed CA and a client certificate is needed
- * three arguments - path to cert PEM file, path to client private key PEM file, path to CA PEM file - if the server certificate is not signed by a system-installed CA and client certificate is needed
-
-## Examples
-
-This is the default SkyDNS setup, with everying specified in full:
-
-~~~
-.:53 {
- etcd skydns.local {
- stubzones
- path /skydns
- endpoint http://localhost:2379
- upstream 8.8.8.8:53 8.8.4.4:53
- }
- prometheus
- cache 160 skydns.local
- loadbalance
- proxy . 8.8.8.8:53 8.8.4.4:53
-}
-~~~
-
-Or a setup where we use `/etc/resolv.conf` as the basis for the proxy and the upstream
-when resolving external pointing CNAMEs.
-
-~~~
-.:53 {
- etcd skydns.local {
- path /skydns
- upstream /etc/resolv.conf
- }
- cache 160 skydns.local
- proxy . /etc/resolv.conf
-}
-~~~
-
-
-### Reverse zones
-
-Reverse zones are supported. You need to make CoreDNS aware of the fact that you are also
-authoritative for the reverse. For instance if you want to add the reverse for 10.0.0.0/24, you'll
-need to add the zone `0.0.10.in-addr.arpa` to the list of zones. (The fun starts with IPv6 reverse zones
-in the ip6.arpa domain.) Showing a snippet of a Corefile:
-
-~~~
- etcd skydns.local 0.0.10.in-addr.arpa {
- stubzones
- ...
-~~~
-
-Next you'll need to populate the zone with reverse records, here we add a reverse for
-10.0.0.127 pointing to reverse.skydns.local.
-
-~~~
-% curl -XPUT http://127.0.0.1:4001/v2/keys/skydns/arpa/in-addr/10/0/0/127 \
- -d value='{"host":"reverse.skydns.local."}'
-~~~
-
-Querying with dig:
-
-~~~
-% dig @localhost -x 10.0.0.127 +short
-reverse.atoom.net.
-~~~
diff --git a/middleware/etcd/cname_test.go b/middleware/etcd/cname_test.go
deleted file mode 100644
index 4c39491fd..000000000
--- a/middleware/etcd/cname_test.go
+++ /dev/null
@@ -1,79 +0,0 @@
-// +build etcd
-
-package etcd
-
-// etcd needs to be running on http://localhost:2379
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
-)
-
-// Check the ordering of returned cname.
-func TestCnameLookup(t *testing.T) {
- etc := newEtcdMiddleware()
-
- for _, serv := range servicesCname {
- set(t, etc, serv.Key, 0, serv)
- defer delete(t, etc, serv.Key)
- }
- for _, tc := range dnsTestCasesCname {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := etc.ServeDNS(ctxt, rec, m)
- if err != nil {
- t.Errorf("expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- if !test.Header(t, tc, resp) {
- t.Logf("%v\n", resp)
- continue
- }
- if !test.Section(t, tc, test.Answer, resp.Answer) {
- t.Logf("%v\n", resp)
- }
- if !test.Section(t, tc, test.Ns, resp.Ns) {
- t.Logf("%v\n", resp)
- }
- if !test.Section(t, tc, test.Extra, resp.Extra) {
- t.Logf("%v\n", resp)
- }
- }
-}
-
-var servicesCname = []*msg.Service{
- {Host: "cname1.region2.skydns.test", Key: "a.server1.dev.region1.skydns.test."},
- {Host: "cname2.region2.skydns.test", Key: "cname1.region2.skydns.test."},
- {Host: "cname3.region2.skydns.test", Key: "cname2.region2.skydns.test."},
- {Host: "cname4.region2.skydns.test", Key: "cname3.region2.skydns.test."},
- {Host: "cname5.region2.skydns.test", Key: "cname4.region2.skydns.test."},
- {Host: "cname6.region2.skydns.test", Key: "cname5.region2.skydns.test."},
- {Host: "endpoint.region2.skydns.test", Key: "cname6.region2.skydns.test."},
- {Host: "10.240.0.1", Key: "endpoint.region2.skydns.test."},
-}
-
-var dnsTestCasesCname = []test.Case{
- {
- Qname: "a.server1.dev.region1.skydns.test.", Qtype: dns.TypeSRV,
- Answer: []dns.RR{
- test.SRV("a.server1.dev.region1.skydns.test. 300 IN SRV 10 100 0 cname1.region2.skydns.test."),
- },
- Extra: []dns.RR{
- test.CNAME("cname1.region2.skydns.test. 300 IN CNAME cname2.region2.skydns.test."),
- test.CNAME("cname2.region2.skydns.test. 300 IN CNAME cname3.region2.skydns.test."),
- test.CNAME("cname3.region2.skydns.test. 300 IN CNAME cname4.region2.skydns.test."),
- test.CNAME("cname4.region2.skydns.test. 300 IN CNAME cname5.region2.skydns.test."),
- test.CNAME("cname5.region2.skydns.test. 300 IN CNAME cname6.region2.skydns.test."),
- test.CNAME("cname6.region2.skydns.test. 300 IN CNAME endpoint.region2.skydns.test."),
- test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.1"),
- },
- },
-}
diff --git a/middleware/etcd/etcd.go b/middleware/etcd/etcd.go
deleted file mode 100644
index 5ef6e0a47..000000000
--- a/middleware/etcd/etcd.go
+++ /dev/null
@@ -1,188 +0,0 @@
-// Package etcd provides the etcd backend middleware.
-package etcd
-
-import (
- "encoding/json"
- "fmt"
- "strings"
- "time"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/middleware/pkg/cache"
- "github.com/coredns/coredns/middleware/pkg/singleflight"
- "github.com/coredns/coredns/middleware/proxy"
- "github.com/coredns/coredns/request"
-
- etcdc "github.com/coreos/etcd/client"
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// Etcd is a middleware talks to an etcd cluster.
-type Etcd struct {
- Next middleware.Handler
- Fallthrough bool
- Zones []string
- PathPrefix string
- Proxy proxy.Proxy // Proxy for looking up names during the resolution process
- Client etcdc.KeysAPI
- Ctx context.Context
- Inflight *singleflight.Group
- Stubmap *map[string]proxy.Proxy // list of proxies for stub resolving.
-
- endpoints []string // Stored here as well, to aid in testing.
-}
-
-// Services implements the ServiceBackend interface.
-func (e *Etcd) Services(state request.Request, exact bool, opt middleware.Options) (services []msg.Service, err error) {
- services, err = e.Records(state, exact)
- if err != nil {
- return
- }
-
- services = msg.Group(services)
- return
-}
-
-// Reverse implements the ServiceBackend interface.
-func (e *Etcd) Reverse(state request.Request, exact bool, opt middleware.Options) (services []msg.Service, err error) {
- return e.Services(state, exact, opt)
-}
-
-// Lookup implements the ServiceBackend interface.
-func (e *Etcd) Lookup(state request.Request, name string, typ uint16) (*dns.Msg, error) {
- return e.Proxy.Lookup(state, name, typ)
-}
-
-// IsNameError implements the ServiceBackend interface.
-func (e *Etcd) IsNameError(err error) bool {
- if ee, ok := err.(etcdc.Error); ok && ee.Code == etcdc.ErrorCodeKeyNotFound {
- return true
- }
- return false
-}
-
-// Records looks up records in etcd. If exact is true, it will lookup just this
-// name. This is used when find matches when completing SRV lookups for instance.
-func (e *Etcd) Records(state request.Request, exact bool) ([]msg.Service, error) {
- name := state.Name()
-
- path, star := msg.PathWithWildcard(name, e.PathPrefix)
- r, err := e.get(path, true)
- if err != nil {
- return nil, err
- }
- segments := strings.Split(msg.Path(name, e.PathPrefix), "/")
- switch {
- case exact && r.Node.Dir:
- return nil, nil
- case r.Node.Dir:
- return e.loopNodes(r.Node.Nodes, segments, star, nil)
- default:
- return e.loopNodes([]*etcdc.Node{r.Node}, segments, false, nil)
- }
-}
-
-// get is a wrapper for client.Get that uses SingleInflight to suppress multiple outstanding queries.
-func (e *Etcd) get(path string, recursive bool) (*etcdc.Response, error) {
-
- hash := cache.Hash([]byte(path))
-
- resp, err := e.Inflight.Do(hash, func() (interface{}, error) {
- ctx, cancel := context.WithTimeout(e.Ctx, etcdTimeout)
- defer cancel()
- r, e := e.Client.Get(ctx, path, &etcdc.GetOptions{Sort: false, Recursive: recursive})
- if e != nil {
- return nil, e
- }
- return r, e
- })
- if err != nil {
- return nil, err
- }
- return resp.(*etcdc.Response), err
-}
-
-// skydns/local/skydns/east/staging/web
-// skydns/local/skydns/west/production/web
-//
-// skydns/local/skydns/*/*/web
-// skydns/local/skydns/*/web
-
-// loopNodes recursively loops through the nodes and returns all the values. The nodes' keyname
-// will be match against any wildcards when star is true.
-func (e *Etcd) loopNodes(ns []*etcdc.Node, nameParts []string, star bool, bx map[msg.Service]bool) (sx []msg.Service, err error) {
- if bx == nil {
- bx = make(map[msg.Service]bool)
- }
-Nodes:
- for _, n := range ns {
- if n.Dir {
- nodes, err := e.loopNodes(n.Nodes, nameParts, star, bx)
- if err != nil {
- return nil, err
- }
- sx = append(sx, nodes...)
- continue
- }
- if star {
- keyParts := strings.Split(n.Key, "/")
- for i, n := range nameParts {
- if i > len(keyParts)-1 {
- // name is longer than key
- continue Nodes
- }
- if n == "*" || n == "any" {
- continue
- }
- if keyParts[i] != n {
- continue Nodes
- }
- }
- }
- serv := new(msg.Service)
- if err := json.Unmarshal([]byte(n.Value), serv); err != nil {
- return nil, fmt.Errorf("%s: %s", n.Key, err.Error())
- }
- b := msg.Service{Host: serv.Host, Port: serv.Port, Priority: serv.Priority, Weight: serv.Weight, Text: serv.Text, Key: n.Key}
- if _, ok := bx[b]; ok {
- continue
- }
- bx[b] = true
-
- serv.Key = n.Key
- serv.TTL = e.TTL(n, serv)
- if serv.Priority == 0 {
- serv.Priority = priority
- }
- sx = append(sx, *serv)
- }
- return sx, nil
-}
-
-// TTL returns the smaller of the etcd TTL and the service's
-// TTL. If neither of these are set (have a zero value), a default is used.
-func (e *Etcd) TTL(node *etcdc.Node, serv *msg.Service) uint32 {
- etcdTTL := uint32(node.TTL)
-
- if etcdTTL == 0 && serv.TTL == 0 {
- return ttl
- }
- if etcdTTL == 0 {
- return serv.TTL
- }
- if serv.TTL == 0 {
- return etcdTTL
- }
- if etcdTTL < serv.TTL {
- return etcdTTL
- }
- return serv.TTL
-}
-
-const (
- priority = 10 // default priority when nothing is set
- ttl = 300 // default ttl when nothing is set
- etcdTimeout = 5 * time.Second
-)
diff --git a/middleware/etcd/group_test.go b/middleware/etcd/group_test.go
deleted file mode 100644
index 43ce4754a..000000000
--- a/middleware/etcd/group_test.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// +build etcd
-
-package etcd
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
-)
-
-func TestGroupLookup(t *testing.T) {
- etc := newEtcdMiddleware()
-
- for _, serv := range servicesGroup {
- set(t, etc, serv.Key, 0, serv)
- defer delete(t, etc, serv.Key)
- }
- for _, tc := range dnsTestCasesGroup {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := etc.ServeDNS(ctxt, rec, m)
- if err != nil {
- t.Errorf("expected no error, got %v\n", err)
- continue
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-// Note the key is encoded as DNS name, while in "reality" it is a etcd path.
-var servicesGroup = []*msg.Service{
- {Host: "127.0.0.1", Key: "a.dom.skydns.test.", Group: "g1"},
- {Host: "127.0.0.2", Key: "b.sub.dom.skydns.test.", Group: "g1"},
-
- {Host: "127.0.0.1", Key: "a.dom2.skydns.test.", Group: "g1"},
- {Host: "127.0.0.2", Key: "b.sub.dom2.skydns.test.", Group: ""},
-
- {Host: "127.0.0.1", Key: "a.dom1.skydns.test.", Group: "g1"},
- {Host: "127.0.0.2", Key: "b.sub.dom1.skydns.test.", Group: "g2"},
-}
-
-var dnsTestCasesGroup = []test.Case{
- // Groups
- {
- // hits the group 'g1' and only includes those records
- Qname: "dom.skydns.test.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.A("dom.skydns.test. 300 IN A 127.0.0.1"),
- test.A("dom.skydns.test. 300 IN A 127.0.0.2"),
- },
- },
- {
- // One has group, the other has not... Include the non-group always.
- Qname: "dom2.skydns.test.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.A("dom2.skydns.test. 300 IN A 127.0.0.1"),
- test.A("dom2.skydns.test. 300 IN A 127.0.0.2"),
- },
- },
- {
- // The groups differ.
- Qname: "dom1.skydns.test.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.A("dom1.skydns.test. 300 IN A 127.0.0.1"),
- },
- },
-}
diff --git a/middleware/etcd/handler.go b/middleware/etcd/handler.go
deleted file mode 100644
index 7857b1955..000000000
--- a/middleware/etcd/handler.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package etcd
-
-import (
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// ServeDNS implements the middleware.Handler interface.
-func (e *Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- opt := middleware.Options{}
- state := request.Request{W: w, Req: r}
-
- name := state.Name()
-
- // We need to check stubzones first, because we may get a request for a zone we
- // are not auth. for *but* do have a stubzone forward for. If we do the stubzone
- // handler will handle the request.
- if e.Stubmap != nil && len(*e.Stubmap) > 0 {
- for zone := range *e.Stubmap {
- if middleware.Name(zone).Matches(name) {
- stub := Stub{Etcd: e, Zone: zone}
- return stub.ServeDNS(ctx, w, r)
- }
- }
- }
-
- zone := middleware.Zones(e.Zones).Matches(state.Name())
- if zone == "" {
- return middleware.NextOrFailure(e.Name(), e.Next, ctx, w, r)
- }
-
- var (
- records, extra []dns.RR
- err error
- )
- switch state.Type() {
- case "A":
- records, err = middleware.A(e, zone, state, nil, opt)
- case "AAAA":
- records, err = middleware.AAAA(e, zone, state, nil, opt)
- case "TXT":
- records, err = middleware.TXT(e, zone, state, opt)
- case "CNAME":
- records, err = middleware.CNAME(e, zone, state, opt)
- case "PTR":
- records, err = middleware.PTR(e, zone, state, opt)
- case "MX":
- records, extra, err = middleware.MX(e, zone, state, opt)
- case "SRV":
- records, extra, err = middleware.SRV(e, zone, state, opt)
- case "SOA":
- records, err = middleware.SOA(e, zone, state, opt)
- case "NS":
- if state.Name() == zone {
- records, extra, err = middleware.NS(e, zone, state, opt)
- break
- }
- fallthrough
- default:
- // Do a fake A lookup, so we can distinguish between NODATA and NXDOMAIN
- _, err = middleware.A(e, zone, state, nil, opt)
- }
-
- if e.IsNameError(err) {
- if e.Fallthrough {
- return middleware.NextOrFailure(e.Name(), e.Next, ctx, w, r)
- }
- // Make err nil when returning here, so we don't log spam for NXDOMAIN.
- return middleware.BackendError(e, zone, dns.RcodeNameError, state, nil /* err */, opt)
- }
- if err != nil {
- return middleware.BackendError(e, zone, dns.RcodeServerFailure, state, err, opt)
- }
-
- if len(records) == 0 {
- return middleware.BackendError(e, zone, dns.RcodeSuccess, state, err, opt)
- }
-
- m := new(dns.Msg)
- m.SetReply(r)
- m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
- m.Answer = append(m.Answer, records...)
- m.Extra = append(m.Extra, extra...)
-
- m = dnsutil.Dedup(m)
- state.SizeAndDo(m)
- m, _ = state.Scrub(m)
- w.WriteMsg(m)
- return dns.RcodeSuccess, nil
-}
-
-// Name implements the Handler interface.
-func (e *Etcd) Name() string { return "etcd" }
diff --git a/middleware/etcd/lookup_test.go b/middleware/etcd/lookup_test.go
deleted file mode 100644
index 010bd66df..000000000
--- a/middleware/etcd/lookup_test.go
+++ /dev/null
@@ -1,273 +0,0 @@
-// +build etcd
-
-package etcd
-
-import (
- "context"
- "encoding/json"
- "testing"
- "time"
-
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/pkg/singleflight"
- "github.com/coredns/coredns/middleware/pkg/tls"
- "github.com/coredns/coredns/middleware/proxy"
- "github.com/coredns/coredns/middleware/test"
-
- etcdc "github.com/coreos/etcd/client"
- "github.com/miekg/dns"
-)
-
-func init() {
- ctxt = context.TODO()
-}
-
-// Note the key is encoded as DNS name, while in "reality" it is a etcd path.
-var services = []*msg.Service{
- {Host: "dev.server1", Port: 8080, Key: "a.server1.dev.region1.skydns.test."},
- {Host: "10.0.0.1", Port: 8080, Key: "a.server1.prod.region1.skydns.test."},
- {Host: "10.0.0.2", Port: 8080, Key: "b.server1.prod.region1.skydns.test."},
- {Host: "::1", Port: 8080, Key: "b.server6.prod.region1.skydns.test."},
- // Unresolvable internal name.
- {Host: "unresolvable.skydns.test", Key: "cname.prod.region1.skydns.test."},
- // Priority.
- {Host: "priority.server1", Priority: 333, Port: 8080, Key: "priority.skydns.test."},
- // Subdomain.
- {Host: "sub.server1", Port: 0, Key: "a.sub.region1.skydns.test."},
- {Host: "sub.server2", Port: 80, Key: "b.sub.region1.skydns.test."},
- {Host: "10.0.0.1", Port: 8080, Key: "c.sub.region1.skydns.test."},
- // Cname loop.
- {Host: "a.cname.skydns.test", Key: "b.cname.skydns.test."},
- {Host: "b.cname.skydns.test", Key: "a.cname.skydns.test."},
- // Nameservers.
- {Host: "10.0.0.2", Key: "a.ns.dns.skydns.test."},
- {Host: "10.0.0.3", Key: "b.ns.dns.skydns.test."},
- // Reverse.
- {Host: "reverse.example.com", Key: "1.0.0.10.in-addr.arpa."}, // 10.0.0.1
-}
-
-var dnsTestCases = []test.Case{
- // SRV Test
- {
- Qname: "a.server1.dev.region1.skydns.test.", Qtype: dns.TypeSRV,
- Answer: []dns.RR{test.SRV("a.server1.dev.region1.skydns.test. 300 SRV 10 100 8080 dev.server1.")},
- },
- // SRV Test (case test)
- {
- Qname: "a.SERVer1.dEv.region1.skydns.tEst.", Qtype: dns.TypeSRV,
- Answer: []dns.RR{test.SRV("a.SERVer1.dEv.region1.skydns.tEst. 300 SRV 10 100 8080 dev.server1.")},
- },
- // NXDOMAIN Test
- {
- Qname: "doesnotexist.skydns.test.", Qtype: dns.TypeA,
- Rcode: dns.RcodeNameError,
- Ns: []dns.RR{
- test.SOA("skydns.test. 300 SOA ns.dns.skydns.test. hostmaster.skydns.test. 0 0 0 0 0"),
- },
- },
- // A Test
- {
- Qname: "a.server1.prod.region1.skydns.test.", Qtype: dns.TypeA,
- Answer: []dns.RR{test.A("a.server1.prod.region1.skydns.test. 300 A 10.0.0.1")},
- },
- // SRV Test where target is IP address
- {
- Qname: "a.server1.prod.region1.skydns.test.", Qtype: dns.TypeSRV,
- Answer: []dns.RR{test.SRV("a.server1.prod.region1.skydns.test. 300 SRV 10 100 8080 a.server1.prod.region1.skydns.test.")},
- Extra: []dns.RR{test.A("a.server1.prod.region1.skydns.test. 300 A 10.0.0.1")},
- },
- // AAAA Test
- {
- Qname: "b.server6.prod.region1.skydns.test.", Qtype: dns.TypeAAAA,
- Answer: []dns.RR{test.AAAA("b.server6.prod.region1.skydns.test. 300 AAAA ::1")},
- },
- // Multiple A Record Test
- {
- Qname: "server1.prod.region1.skydns.test.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.A("server1.prod.region1.skydns.test. 300 A 10.0.0.1"),
- test.A("server1.prod.region1.skydns.test. 300 A 10.0.0.2"),
- },
- },
- // Priority Test
- {
- Qname: "priority.skydns.test.", Qtype: dns.TypeSRV,
- Answer: []dns.RR{test.SRV("priority.skydns.test. 300 SRV 333 100 8080 priority.server1.")},
- },
- // Subdomain Test
- {
- Qname: "sub.region1.skydns.test.", Qtype: dns.TypeSRV,
- Answer: []dns.RR{
- test.SRV("sub.region1.skydns.test. 300 IN SRV 10 33 0 sub.server1."),
- test.SRV("sub.region1.skydns.test. 300 IN SRV 10 33 80 sub.server2."),
- test.SRV("sub.region1.skydns.test. 300 IN SRV 10 33 8080 c.sub.region1.skydns.test."),
- },
- Extra: []dns.RR{test.A("c.sub.region1.skydns.test. 300 IN A 10.0.0.1")},
- },
- // CNAME (unresolvable internal name)
- {
- Qname: "cname.prod.region1.skydns.test.", Qtype: dns.TypeA,
- Ns: []dns.RR{test.SOA("skydns.test. 300 SOA ns.dns.skydns.test. hostmaster.skydns.test. 0 0 0 0 0")},
- },
- // Wildcard Test
- {
- Qname: "*.region1.skydns.test.", Qtype: dns.TypeSRV,
- Answer: []dns.RR{
- test.SRV("*.region1.skydns.test. 300 IN SRV 10 12 0 sub.server1."),
- test.SRV("*.region1.skydns.test. 300 IN SRV 10 12 0 unresolvable.skydns.test."),
- test.SRV("*.region1.skydns.test. 300 IN SRV 10 12 80 sub.server2."),
- test.SRV("*.region1.skydns.test. 300 IN SRV 10 12 8080 a.server1.prod.region1.skydns.test."),
- test.SRV("*.region1.skydns.test. 300 IN SRV 10 12 8080 b.server1.prod.region1.skydns.test."),
- test.SRV("*.region1.skydns.test. 300 IN SRV 10 12 8080 b.server6.prod.region1.skydns.test."),
- test.SRV("*.region1.skydns.test. 300 IN SRV 10 12 8080 c.sub.region1.skydns.test."),
- test.SRV("*.region1.skydns.test. 300 IN SRV 10 12 8080 dev.server1."),
- },
- Extra: []dns.RR{
- test.A("a.server1.prod.region1.skydns.test. 300 IN A 10.0.0.1"),
- test.A("b.server1.prod.region1.skydns.test. 300 IN A 10.0.0.2"),
- test.AAAA("b.server6.prod.region1.skydns.test. 300 IN AAAA ::1"),
- test.A("c.sub.region1.skydns.test. 300 IN A 10.0.0.1"),
- },
- },
- // Wildcard Test
- {
- Qname: "prod.*.skydns.test.", Qtype: dns.TypeSRV,
- Answer: []dns.RR{
-
- test.SRV("prod.*.skydns.test. 300 IN SRV 10 25 0 unresolvable.skydns.test."),
- test.SRV("prod.*.skydns.test. 300 IN SRV 10 25 8080 a.server1.prod.region1.skydns.test."),
- test.SRV("prod.*.skydns.test. 300 IN SRV 10 25 8080 b.server1.prod.region1.skydns.test."),
- test.SRV("prod.*.skydns.test. 300 IN SRV 10 25 8080 b.server6.prod.region1.skydns.test."),
- },
- Extra: []dns.RR{
- test.A("a.server1.prod.region1.skydns.test. 300 IN A 10.0.0.1"),
- test.A("b.server1.prod.region1.skydns.test. 300 IN A 10.0.0.2"),
- test.AAAA("b.server6.prod.region1.skydns.test. 300 IN AAAA ::1"),
- },
- },
- // Wildcard Test
- {
- Qname: "prod.any.skydns.test.", Qtype: dns.TypeSRV,
- Answer: []dns.RR{
- test.SRV("prod.any.skydns.test. 300 IN SRV 10 25 0 unresolvable.skydns.test."),
- test.SRV("prod.any.skydns.test. 300 IN SRV 10 25 8080 a.server1.prod.region1.skydns.test."),
- test.SRV("prod.any.skydns.test. 300 IN SRV 10 25 8080 b.server1.prod.region1.skydns.test."),
- test.SRV("prod.any.skydns.test. 300 IN SRV 10 25 8080 b.server6.prod.region1.skydns.test."),
- },
- Extra: []dns.RR{
- test.A("a.server1.prod.region1.skydns.test. 300 IN A 10.0.0.1"),
- test.A("b.server1.prod.region1.skydns.test. 300 IN A 10.0.0.2"),
- test.AAAA("b.server6.prod.region1.skydns.test. 300 IN AAAA ::1"),
- },
- },
- // CNAME loop detection
- {
- Qname: "a.cname.skydns.test.", Qtype: dns.TypeA,
- Ns: []dns.RR{test.SOA("skydns.test. 300 SOA ns.dns.skydns.test. hostmaster.skydns.test. 1407441600 28800 7200 604800 60")},
- },
- // NODATA Test
- {
- Qname: "a.server1.dev.region1.skydns.test.", Qtype: dns.TypeTXT,
- Ns: []dns.RR{test.SOA("skydns.test. 300 SOA ns.dns.skydns.test. hostmaster.skydns.test. 0 0 0 0 0")},
- },
- // NODATA Test
- {
- Qname: "a.server1.dev.region1.skydns.test.", Qtype: dns.TypeHINFO,
- Ns: []dns.RR{test.SOA("skydns.test. 300 SOA ns.dns.skydns.test. hostmaster.skydns.test. 0 0 0 0 0")},
- },
- // NXDOMAIN Test
- {
- Qname: "a.server1.nonexistent.region1.skydns.test.", Qtype: dns.TypeHINFO, Rcode: dns.RcodeNameError,
- Ns: []dns.RR{test.SOA("skydns.test. 300 SOA ns.dns.skydns.test. hostmaster.skydns.test. 0 0 0 0 0")},
- },
- {
- Qname: "skydns.test.", Qtype: dns.TypeSOA,
- Answer: []dns.RR{test.SOA("skydns.test. 300 IN SOA ns.dns.skydns.test. hostmaster.skydns.test. 1460498836 14400 3600 604800 60")},
- },
- // NS Record Test
- {
- Qname: "skydns.test.", Qtype: dns.TypeNS,
- Answer: []dns.RR{
- test.NS("skydns.test. 300 NS a.ns.dns.skydns.test."),
- test.NS("skydns.test. 300 NS b.ns.dns.skydns.test."),
- },
- Extra: []dns.RR{
- test.A("a.ns.dns.skydns.test. 300 A 10.0.0.2"),
- test.A("b.ns.dns.skydns.test. 300 A 10.0.0.3"),
- },
- },
- // NS Record Test
- {
- Qname: "a.skydns.test.", Qtype: dns.TypeNS, Rcode: dns.RcodeNameError,
- Ns: []dns.RR{test.SOA("skydns.test. 300 IN SOA ns.dns.skydns.test. hostmaster.skydns.test. 1460498836 14400 3600 604800 60")},
- },
- // A Record For NS Record Test
- {
- Qname: "ns.dns.skydns.test.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.A("ns.dns.skydns.test. 300 A 10.0.0.2"),
- test.A("ns.dns.skydns.test. 300 A 10.0.0.3"),
- },
- },
- {
- Qname: "skydns_extra.test.", Qtype: dns.TypeSOA,
- Answer: []dns.RR{test.SOA("skydns_extra.test. 300 IN SOA ns.dns.skydns_extra.test. hostmaster.skydns_extra.test. 1460498836 14400 3600 604800 60")},
- },
- // Reverse lookup
- {
- Qname: "1.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
- Answer: []dns.RR{test.PTR("1.0.0.10.in-addr.arpa. 300 PTR reverse.example.com.")},
- },
-}
-
-func newEtcdMiddleware() *Etcd {
- ctxt = context.TODO()
-
- endpoints := []string{"http://localhost:2379"}
- tlsc, _ := tls.NewTLSConfigFromArgs()
- client, _ := newEtcdClient(endpoints, tlsc)
-
- return &Etcd{
- Proxy: proxy.NewLookup([]string{"8.8.8.8:53"}),
- PathPrefix: "skydns",
- Ctx: context.Background(),
- Inflight: &singleflight.Group{},
- Zones: []string{"skydns.test.", "skydns_extra.test.", "in-addr.arpa."},
- Client: client,
- }
-}
-
-func set(t *testing.T, e *Etcd, k string, ttl time.Duration, m *msg.Service) {
- b, err := json.Marshal(m)
- if err != nil {
- t.Fatal(err)
- }
- path, _ := msg.PathWithWildcard(k, e.PathPrefix)
- e.Client.Set(ctxt, path, string(b), &etcdc.SetOptions{TTL: ttl})
-}
-
-func delete(t *testing.T, e *Etcd, k string) {
- path, _ := msg.PathWithWildcard(k, e.PathPrefix)
- e.Client.Delete(ctxt, path, &etcdc.DeleteOptions{Recursive: false})
-}
-
-func TestLookup(t *testing.T) {
- etc := newEtcdMiddleware()
- for _, serv := range services {
- set(t, etc, serv.Key, 0, serv)
- defer delete(t, etc, serv.Key)
- }
-
- for _, tc := range dnsTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- etc.ServeDNS(ctxt, rec, m)
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-var ctxt context.Context
diff --git a/middleware/etcd/msg/path.go b/middleware/etcd/msg/path.go
deleted file mode 100644
index 2184b9fcd..000000000
--- a/middleware/etcd/msg/path.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package msg
-
-import (
- "path"
- "strings"
-
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
-
- "github.com/miekg/dns"
-)
-
-// Path converts a domainname to an etcd path. If s looks like service.staging.skydns.local.,
-// the resulting key will be /skydns/local/skydns/staging/service .
-func Path(s, prefix string) string {
- l := dns.SplitDomainName(s)
- for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
- l[i], l[j] = l[j], l[i]
- }
- return path.Join(append([]string{"/" + prefix + "/"}, l...)...)
-}
-
-// Domain is the opposite of Path.
-func Domain(s string) string {
- l := strings.Split(s, "/")
- // start with 1, to strip /skydns
- for i, j := 1, len(l)-1; i < j; i, j = i+1, j-1 {
- l[i], l[j] = l[j], l[i]
- }
- return dnsutil.Join(l[1 : len(l)-1])
-}
-
-// PathWithWildcard ascts as Path, but if a name contains wildcards (* or any), the name will be
-// chopped of before the (first) wildcard, and we do a highler evel search and
-// later find the matching names. So service.*.skydns.local, will look for all
-// services under skydns.local and will later check for names that match
-// service.*.skydns.local. If a wildcard is found the returned bool is true.
-func PathWithWildcard(s, prefix string) (string, bool) {
- l := dns.SplitDomainName(s)
- for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
- l[i], l[j] = l[j], l[i]
- }
- for i, k := range l {
- if k == "*" || k == "any" {
- return path.Join(append([]string{"/" + prefix + "/"}, l[:i]...)...), true
- }
- }
- return path.Join(append([]string{"/" + prefix + "/"}, l...)...), false
-}
diff --git a/middleware/etcd/msg/path_test.go b/middleware/etcd/msg/path_test.go
deleted file mode 100644
index a9ec59713..000000000
--- a/middleware/etcd/msg/path_test.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package msg
-
-import "testing"
-
-func TestPath(t *testing.T) {
- for _, path := range []string{"mydns", "skydns"} {
- result := Path("service.staging.skydns.local.", path)
- if result != "/"+path+"/local/skydns/staging/service" {
- t.Errorf("Failure to get domain's path with prefix: %s", result)
- }
- }
-}
diff --git a/middleware/etcd/msg/service.go b/middleware/etcd/msg/service.go
deleted file mode 100644
index 9250cb634..000000000
--- a/middleware/etcd/msg/service.go
+++ /dev/null
@@ -1,203 +0,0 @@
-// Package msg defines the Service structure which is used for service discovery.
-package msg
-
-import (
- "fmt"
- "net"
- "strings"
-
- "github.com/miekg/dns"
-)
-
-// Service defines a discoverable service in etcd. It is the rdata from a SRV
-// record, but with a twist. Host (Target in SRV) must be a domain name, but
-// if it looks like an IP address (4/6), we will treat it like an IP address.
-type Service struct {
- Host string `json:"host,omitempty"`
- Port int `json:"port,omitempty"`
- Priority int `json:"priority,omitempty"`
- Weight int `json:"weight,omitempty"`
- Text string `json:"text,omitempty"`
- Mail bool `json:"mail,omitempty"` // Be an MX record. Priority becomes Preference.
- TTL uint32 `json:"ttl,omitempty"`
-
- // When a SRV record with a "Host: IP-address" is added, we synthesize
- // a srv.Target domain name. Normally we convert the full Key where
- // the record lives to a DNS name and use this as the srv.Target. When
- // TargetStrip > 0 we strip the left most TargetStrip labels from the
- // DNS name.
- TargetStrip int `json:"targetstrip,omitempty"`
-
- // Group is used to group (or *not* to group) different services
- // together. Services with an identical Group are returned in the same
- // answer.
- Group string `json:"group,omitempty"`
-
- // Etcd key where we found this service and ignored from json un-/marshalling
- Key string `json:"-"`
-}
-
-// RR returns an RR representation of s. It is in a condensed form to minimize space
-// when this is returned in a DNS message.
-// The RR will look like:
-// 1.rails.production.east.skydns.local. 300 CH TXT "service1.example.com:8080(10,0,,false)[0,]"
-// etcd Key Ttl Host:Port < see below >
-// between parens: (Priority, Weight, Text (only first 200 bytes!), Mail)
-// between blockquotes: [TargetStrip,Group]
-// If the record is synthesised by CoreDNS (i.e. no lookup in etcd happened):
-//
-// TODO(miek): what to put here?
-//
-func (s *Service) RR() *dns.TXT {
- l := len(s.Text)
- if l > 200 {
- l = 200
- }
- t := new(dns.TXT)
- t.Hdr.Class = dns.ClassCHAOS
- t.Hdr.Ttl = s.TTL
- t.Hdr.Rrtype = dns.TypeTXT
- t.Hdr.Name = Domain(s.Key)
-
- t.Txt = make([]string, 1)
- t.Txt[0] = fmt.Sprintf("%s:%d(%d,%d,%s,%t)[%d,%s]",
- s.Host, s.Port,
- s.Priority, s.Weight, s.Text[:l], s.Mail,
- s.TargetStrip, s.Group)
- return t
-}
-
-// NewSRV returns a new SRV record based on the Service.
-func (s *Service) NewSRV(name string, weight uint16) *dns.SRV {
- host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
-
- return &dns.SRV{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeSRV, Class: dns.ClassINET, Ttl: s.TTL},
- Priority: uint16(s.Priority), Weight: weight, Port: uint16(s.Port), Target: dns.Fqdn(host)}
-}
-
-// NewMX returns a new MX record based on the Service.
-func (s *Service) NewMX(name string) *dns.MX {
- host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
-
- return &dns.MX{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: s.TTL},
- Preference: uint16(s.Priority), Mx: host}
-}
-
-// NewA returns a new A record based on the Service.
-func (s *Service) NewA(name string, ip net.IP) *dns.A {
- return &dns.A{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: s.TTL}, A: ip}
-}
-
-// NewAAAA returns a new AAAA record based on the Service.
-func (s *Service) NewAAAA(name string, ip net.IP) *dns.AAAA {
- return &dns.AAAA{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: s.TTL}, AAAA: ip}
-}
-
-// NewCNAME returns a new CNAME record based on the Service.
-func (s *Service) NewCNAME(name string, target string) *dns.CNAME {
- return &dns.CNAME{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: s.TTL}, Target: dns.Fqdn(target)}
-}
-
-// NewTXT returns a new TXT record based on the Service.
-func (s *Service) NewTXT(name string) *dns.TXT {
- return &dns.TXT{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: s.TTL}, Txt: split255(s.Text)}
-}
-
-// NewPTR returns a new PTR record based on the Service.
-func (s *Service) NewPTR(name string, target string) *dns.PTR {
- return &dns.PTR{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: s.TTL}, Ptr: dns.Fqdn(target)}
-}
-
-// NewNS returns a new NS record based on the Service.
-func (s *Service) NewNS(name string) *dns.NS {
- host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)
- return &dns.NS{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: s.TTL}, Ns: host}
-}
-
-// Group checks the services in sx, it looks for a Group attribute on the shortest
-// keys. If there are multiple shortest keys *and* the group attribute disagrees (and
-// is not empty), we don't consider it a group.
-// If a group is found, only services with *that* group (or no group) will be returned.
-func Group(sx []Service) []Service {
- if len(sx) == 0 {
- return sx
- }
-
- // Shortest key with group attribute sets the group for this set.
- group := sx[0].Group
- slashes := strings.Count(sx[0].Key, "/")
- length := make([]int, len(sx))
- for i, s := range sx {
- x := strings.Count(s.Key, "/")
- length[i] = x
- if x < slashes {
- if s.Group == "" {
- break
- }
- slashes = x
- group = s.Group
- }
- }
-
- if group == "" {
- return sx
- }
-
- ret := []Service{} // with slice-tricks in sx we can prolly save this allocation (TODO)
-
- for i, s := range sx {
- if s.Group == "" {
- ret = append(ret, s)
- continue
- }
-
- // Disagreement on the same level
- if length[i] == slashes && s.Group != group {
- return sx
- }
-
- if s.Group == group {
- ret = append(ret, s)
- }
- }
- return ret
-}
-
-// Split255 splits a string into 255 byte chunks.
-func split255(s string) []string {
- if len(s) < 255 {
- return []string{s}
- }
- sx := []string{}
- p, i := 0, 255
- for {
- if i <= len(s) {
- sx = append(sx, s[p:i])
- } else {
- sx = append(sx, s[p:])
- break
-
- }
- p, i = p+255, i+255
- }
-
- return sx
-}
-
-// targetStrip strips "targetstrip" labels from the left side of the fully qualified name.
-func targetStrip(name string, targetStrip int) string {
- if targetStrip == 0 {
- return name
- }
-
- offset, end := 0, false
- for i := 0; i < targetStrip; i++ {
- offset, end = dns.NextLabel(name, offset)
- }
- if end {
- // We overshot the name, use the orignal one.
- offset = 0
- }
- name = name[offset:]
- return name
-}
diff --git a/middleware/etcd/msg/service_test.go b/middleware/etcd/msg/service_test.go
deleted file mode 100644
index 0c19ba95b..000000000
--- a/middleware/etcd/msg/service_test.go
+++ /dev/null
@@ -1,125 +0,0 @@
-package msg
-
-import "testing"
-
-func TestSplit255(t *testing.T) {
- xs := split255("abc")
- if len(xs) != 1 && xs[0] != "abc" {
- t.Errorf("Failure to split abc")
- }
- s := ""
- for i := 0; i < 255; i++ {
- s += "a"
- }
- xs = split255(s)
- if len(xs) != 1 && xs[0] != s {
- t.Errorf("failure to split 255 char long string")
- }
- s += "b"
- xs = split255(s)
- if len(xs) != 2 || xs[1] != "b" {
- t.Errorf("failure to split 256 char long string: %d", len(xs))
- }
- for i := 0; i < 255; i++ {
- s += "a"
- }
- xs = split255(s)
- if len(xs) != 3 || xs[2] != "a" {
- t.Errorf("failure to split 510 char long string: %d", len(xs))
- }
-}
-
-func TestGroup(t *testing.T) {
- // Key are in the wrong order, but for this test it does not matter.
- sx := Group(
- []Service{
- {Host: "127.0.0.1", Group: "g1", Key: "b/sub/dom1/skydns/test"},
- {Host: "127.0.0.2", Group: "g2", Key: "a/dom1/skydns/test"},
- },
- )
- // Expecting to return the shortest key with a Group attribute.
- if len(sx) != 1 {
- t.Fatalf("failure to group zeroth set: %v", sx)
- }
- if sx[0].Key != "a/dom1/skydns/test" {
- t.Fatalf("failure to group zeroth set: %v, wrong Key", sx)
- }
-
- // Groups disagree, so we will not do anything.
- sx = Group(
- []Service{
- {Host: "server1", Group: "g1", Key: "region1/skydns/test"},
- {Host: "server2", Group: "g2", Key: "region1/skydns/test"},
- },
- )
- if len(sx) != 2 {
- t.Fatalf("failure to group first set: %v", sx)
- }
-
- // Group is g1, include only the top-level one.
- sx = Group(
- []Service{
- {Host: "server1", Group: "g1", Key: "a/dom/region1/skydns/test"},
- {Host: "server2", Group: "g2", Key: "a/subdom/dom/region1/skydns/test"},
- },
- )
- if len(sx) != 1 {
- t.Fatalf("failure to group second set: %v", sx)
- }
-
- // Groupless services must be included.
- sx = Group(
- []Service{
- {Host: "server1", Group: "g1", Key: "a/dom/region1/skydns/test"},
- {Host: "server2", Group: "g2", Key: "a/subdom/dom/region1/skydns/test"},
- {Host: "server2", Group: "", Key: "b/subdom/dom/region1/skydns/test"},
- },
- )
- if len(sx) != 2 {
- t.Fatalf("failure to group third set: %v", sx)
- }
-
- // Empty group on the highest level: include that one also.
- sx = Group(
- []Service{
- {Host: "server1", Group: "g1", Key: "a/dom/region1/skydns/test"},
- {Host: "server1", Group: "", Key: "b/dom/region1/skydns/test"},
- {Host: "server2", Group: "g2", Key: "a/subdom/dom/region1/skydns/test"},
- },
- )
- if len(sx) != 2 {
- t.Fatalf("failure to group fourth set: %v", sx)
- }
-
- // Empty group on the highest level: include that one also, and the rest.
- sx = Group(
- []Service{
- {Host: "server1", Group: "g5", Key: "a/dom/region1/skydns/test"},
- {Host: "server1", Group: "", Key: "b/dom/region1/skydns/test"},
- {Host: "server2", Group: "g5", Key: "a/subdom/dom/region1/skydns/test"},
- },
- )
- if len(sx) != 3 {
- t.Fatalf("failure to group fith set: %v", sx)
- }
-
- // One group.
- sx = Group(
- []Service{
- {Host: "server1", Group: "g6", Key: "a/dom/region1/skydns/test"},
- },
- )
- if len(sx) != 1 {
- t.Fatalf("failure to group sixth set: %v", sx)
- }
-
- // No group, once service
- sx = Group(
- []Service{
- {Host: "server1", Key: "a/dom/region1/skydns/test"},
- },
- )
- if len(sx) != 1 {
- t.Fatalf("failure to group seventh set: %v", sx)
- }
-}
diff --git a/middleware/etcd/msg/type.go b/middleware/etcd/msg/type.go
deleted file mode 100644
index 7f3bfdbb9..000000000
--- a/middleware/etcd/msg/type.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package msg
-
-import (
- "net"
-
- "github.com/miekg/dns"
-)
-
-// HostType returns the DNS type of what is encoded in the Service Host field. We're reusing
-// dns.TypeXXX to not reinvent a new set of identifiers.
-//
-// dns.TypeA: the service's Host field contains an A record.
-// dns.TypeAAAA: the service's Host field contains an AAAA record.
-// dns.TypeCNAME: the service's Host field contains a name.
-//
-// Note that a service can double/triple as a TXT record or MX record.
-func (s *Service) HostType() (what uint16, normalized net.IP) {
-
- ip := net.ParseIP(s.Host)
-
- switch {
- case ip == nil:
- return dns.TypeCNAME, nil
-
- case ip.To4() != nil:
- return dns.TypeA, ip.To4()
-
- case ip.To4() == nil:
- return dns.TypeAAAA, ip.To16()
- }
- // This should never be reached.
- return dns.TypeNone, nil
-}
diff --git a/middleware/etcd/msg/type_test.go b/middleware/etcd/msg/type_test.go
deleted file mode 100644
index bad1eead0..000000000
--- a/middleware/etcd/msg/type_test.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package msg
-
-import (
- "testing"
-
- "github.com/miekg/dns"
-)
-
-func TestType(t *testing.T) {
- tests := []struct {
- serv Service
- expectedType uint16
- }{
- {Service{Host: "example.org"}, dns.TypeCNAME},
- {Service{Host: "127.0.0.1"}, dns.TypeA},
- {Service{Host: "2000::3"}, dns.TypeAAAA},
- {Service{Host: "2000..3"}, dns.TypeCNAME},
- {Service{Host: "127.0.0.257"}, dns.TypeCNAME},
- {Service{Host: "127.0.0.252", Mail: true}, dns.TypeA},
- {Service{Host: "127.0.0.252", Mail: true, Text: "a"}, dns.TypeA},
- {Service{Host: "127.0.0.254", Mail: false, Text: "a"}, dns.TypeA},
- }
-
- for i, tc := range tests {
- what, _ := tc.serv.HostType()
- if what != tc.expectedType {
- t.Errorf("Test %d: Expected what %v, but got %v", i, tc.expectedType, what)
- }
- }
-
-}
diff --git a/middleware/etcd/multi_test.go b/middleware/etcd/multi_test.go
deleted file mode 100644
index 301b2c49e..000000000
--- a/middleware/etcd/multi_test.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// +build etcd
-
-package etcd
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
-)
-
-func TestMultiLookup(t *testing.T) {
- etc := newEtcdMiddleware()
- etc.Zones = []string{"skydns.test.", "miek.nl."}
- etc.Fallthrough = true
- etc.Next = test.ErrorHandler()
-
- for _, serv := range servicesMulti {
- set(t, etc, serv.Key, 0, serv)
- defer delete(t, etc, serv.Key)
- }
- for _, tc := range dnsTestCasesMulti {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := etc.ServeDNS(ctxt, rec, m)
- if err != nil {
- t.Errorf("expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-// Note the key is encoded as DNS name, while in "reality" it is a etcd path.
-var servicesMulti = []*msg.Service{
- {Host: "dev.server1", Port: 8080, Key: "a.server1.dev.region1.skydns.test."},
- {Host: "dev.server1", Port: 8080, Key: "a.server1.dev.region1.miek.nl."},
- {Host: "dev.server1", Port: 8080, Key: "a.server1.dev.region1.example.org."},
-}
-
-var dnsTestCasesMulti = []test.Case{
- {
- Qname: "a.server1.dev.region1.skydns.test.", Qtype: dns.TypeSRV,
- Answer: []dns.RR{test.SRV("a.server1.dev.region1.skydns.test. 300 SRV 10 100 8080 dev.server1.")},
- },
- {
- Qname: "a.server1.dev.region1.miek.nl.", Qtype: dns.TypeSRV,
- Answer: []dns.RR{test.SRV("a.server1.dev.region1.miek.nl. 300 SRV 10 100 8080 dev.server1.")},
- },
- {
- Qname: "a.server1.dev.region1.example.org.", Qtype: dns.TypeSRV, Rcode: dns.RcodeServerFailure,
- },
-}
diff --git a/middleware/etcd/other_test.go b/middleware/etcd/other_test.go
deleted file mode 100644
index b3849b9f7..000000000
--- a/middleware/etcd/other_test.go
+++ /dev/null
@@ -1,150 +0,0 @@
-// +build etcd
-
-// tests mx and txt records
-
-package etcd
-
-import (
- "fmt"
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
-)
-
-func TestOtherLookup(t *testing.T) {
- etc := newEtcdMiddleware()
-
- for _, serv := range servicesOther {
- set(t, etc, serv.Key, 0, serv)
- defer delete(t, etc, serv.Key)
- }
- for _, tc := range dnsTestCasesOther {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := etc.ServeDNS(ctxt, rec, m)
- if err != nil {
- t.Errorf("expected no error, got %v\n", err)
- continue
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-// Note the key is encoded as DNS name, while in "reality" it is a etcd path.
-var servicesOther = []*msg.Service{
- {Host: "dev.server1", Port: 8080, Key: "a.server1.dev.region1.skydns.test."},
-
- // mx
- {Host: "mx.skydns.test", Priority: 50, Mail: true, Key: "a.mail.skydns.test."},
- {Host: "mx.miek.nl", Priority: 50, Mail: true, Key: "b.mail.skydns.test."},
- {Host: "a.ipaddr.skydns.test", Priority: 30, Mail: true, Key: "a.mx.skydns.test."},
-
- {Host: "a.ipaddr.skydns.test", Mail: true, Key: "a.mx2.skydns.test."},
- {Host: "b.ipaddr.skydns.test", Mail: true, Key: "b.mx2.skydns.test."},
-
- {Host: "a.ipaddr.skydns.test", Priority: 20, Mail: true, Key: "a.mx3.skydns.test."},
- {Host: "a.ipaddr.skydns.test", Priority: 30, Mail: true, Key: "b.mx3.skydns.test."},
-
- {Host: "172.16.1.1", Key: "a.ipaddr.skydns.test."},
- {Host: "172.16.1.2", Key: "b.ipaddr.skydns.test."},
-
- // txt
- {Text: "abc", Key: "a1.txt.skydns.test."},
- {Text: "abc abc", Key: "a2.txt.skydns.test."},
- // txt sizes
- {Text: strings.Repeat("0", 400), Key: "large400.skydns.test."},
- {Text: strings.Repeat("0", 600), Key: "large600.skydns.test."},
- {Text: strings.Repeat("0", 2000), Key: "large2000.skydns.test."},
-
- // duplicate ip address
- {Host: "10.11.11.10", Key: "http.multiport.http.skydns.test.", Port: 80},
- {Host: "10.11.11.10", Key: "https.multiport.http.skydns.test.", Port: 443},
-}
-
-var dnsTestCasesOther = []test.Case{
- // MX Tests
- {
- // NODATA as this is not an Mail: true record.
- Qname: "a.server1.dev.region1.skydns.test.", Qtype: dns.TypeMX,
- Ns: []dns.RR{
- test.SOA("skydns.test. 300 SOA ns.dns.skydns.test. hostmaster.skydns.test. 0 0 0 0 0"),
- },
- },
- {
- Qname: "a.mail.skydns.test.", Qtype: dns.TypeMX,
- Answer: []dns.RR{test.MX("a.mail.skydns.test. 300 IN MX 50 mx.skydns.test.")},
- Extra: []dns.RR{
- test.A("a.ipaddr.skydns.test. 300 IN A 172.16.1.1"),
- test.CNAME("mx.skydns.test. 300 IN CNAME a.ipaddr.skydns.test."),
- },
- },
- {
- Qname: "mx2.skydns.test.", Qtype: dns.TypeMX,
- Answer: []dns.RR{
- test.MX("mx2.skydns.test. 300 IN MX 10 a.ipaddr.skydns.test."),
- test.MX("mx2.skydns.test. 300 IN MX 10 b.ipaddr.skydns.test."),
- },
- Extra: []dns.RR{
- test.A("a.ipaddr.skydns.test. 300 A 172.16.1.1"),
- test.A("b.ipaddr.skydns.test. 300 A 172.16.1.2"),
- },
- },
- // different priority, same host
- {
- Qname: "mx3.skydns.test.", Qtype: dns.TypeMX,
- Answer: []dns.RR{
- test.MX("mx3.skydns.test. 300 IN MX 20 a.ipaddr.skydns.test."),
- test.MX("mx3.skydns.test. 300 IN MX 30 a.ipaddr.skydns.test."),
- },
- Extra: []dns.RR{
- test.A("a.ipaddr.skydns.test. 300 A 172.16.1.1"),
- },
- },
- // Txt
- {
- Qname: "a1.txt.skydns.test.", Qtype: dns.TypeTXT,
- Answer: []dns.RR{
- test.TXT("a1.txt.skydns.test. 300 IN TXT \"abc\""),
- },
- },
- {
- Qname: "a2.txt.skydns.test.", Qtype: dns.TypeTXT,
- Answer: []dns.RR{
- test.TXT("a2.txt.skydns.test. 300 IN TXT \"abc abc\""),
- },
- },
- // Large txt less than 512
- {
- Qname: "large400.skydns.test.", Qtype: dns.TypeTXT,
- Answer: []dns.RR{
- test.TXT(fmt.Sprintf("large400.skydns.test. 300 IN TXT \"%s\"", strings.Repeat("0", 400))),
- },
- },
- // Large txt greater than 512 (UDP)
- {
- Qname: "large600.skydns.test.", Qtype: dns.TypeTXT,
- Answer: []dns.RR{
- test.TXT(fmt.Sprintf("large600.skydns.test. 300 IN TXT \"%s\"", strings.Repeat("0", 600))),
- },
- },
- // Large txt greater than 1500 (typical Ethernet)
- {
- Qname: "large2000.skydns.test.", Qtype: dns.TypeTXT,
- Answer: []dns.RR{
- test.TXT(fmt.Sprintf("large2000.skydns.test. 300 IN TXT \"%s\"", strings.Repeat("0", 2000))),
- },
- },
- // Duplicate IP address test
- {
- Qname: "multiport.http.skydns.test.", Qtype: dns.TypeA,
- Answer: []dns.RR{test.A("multiport.http.skydns.test. 300 IN A 10.11.11.10")},
- },
-}
diff --git a/middleware/etcd/setup.go b/middleware/etcd/setup.go
deleted file mode 100644
index 7cbcfd2ea..000000000
--- a/middleware/etcd/setup.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package etcd
-
-import (
- "crypto/tls"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/middleware/pkg/singleflight"
- mwtls "github.com/coredns/coredns/middleware/pkg/tls"
- "github.com/coredns/coredns/middleware/proxy"
-
- etcdc "github.com/coreos/etcd/client"
- "github.com/mholt/caddy"
- "golang.org/x/net/context"
-)
-
-func init() {
- caddy.RegisterPlugin("etcd", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- e, stubzones, err := etcdParse(c)
- if err != nil {
- return middleware.Error("etcd", err)
- }
-
- if stubzones {
- c.OnStartup(func() error {
- e.UpdateStubZones()
- return nil
- })
- }
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- e.Next = next
- return e
- })
-
- return nil
-}
-
-func etcdParse(c *caddy.Controller) (*Etcd, bool, error) {
- stub := make(map[string]proxy.Proxy)
- etc := Etcd{
- // Don't default to a proxy for lookups.
- // Proxy: proxy.NewLookup([]string{"8.8.8.8:53", "8.8.4.4:53"}),
- PathPrefix: "skydns",
- Ctx: context.Background(),
- Inflight: &singleflight.Group{},
- Stubmap: &stub,
- }
- var (
- tlsConfig *tls.Config
- err error
- endpoints = []string{defaultEndpoint}
- stubzones = false
- )
- for c.Next() {
- etc.Zones = c.RemainingArgs()
- if len(etc.Zones) == 0 {
- etc.Zones = make([]string, len(c.ServerBlockKeys))
- copy(etc.Zones, c.ServerBlockKeys)
- }
- for i, str := range etc.Zones {
- etc.Zones[i] = middleware.Host(str).Normalize()
- }
-
- if c.NextBlock() {
- for {
- switch c.Val() {
- case "stubzones":
- stubzones = true
- case "fallthrough":
- etc.Fallthrough = true
- case "debug":
- /* it is a noop now */
- case "path":
- if !c.NextArg() {
- return &Etcd{}, false, c.ArgErr()
- }
- etc.PathPrefix = c.Val()
- case "endpoint":
- args := c.RemainingArgs()
- if len(args) == 0 {
- return &Etcd{}, false, c.ArgErr()
- }
- endpoints = args
- case "upstream":
- args := c.RemainingArgs()
- if len(args) == 0 {
- return &Etcd{}, false, c.ArgErr()
- }
- ups, err := dnsutil.ParseHostPortOrFile(args...)
- if err != nil {
- return &Etcd{}, false, err
- }
- etc.Proxy = proxy.NewLookup(ups)
- case "tls": // cert key cacertfile
- args := c.RemainingArgs()
- tlsConfig, err = mwtls.NewTLSConfigFromArgs(args...)
- if err != nil {
- return &Etcd{}, false, err
- }
- default:
- if c.Val() != "}" {
- return &Etcd{}, false, c.Errf("unknown property '%s'", c.Val())
- }
- }
-
- if !c.Next() {
- break
- }
- }
-
- }
- client, err := newEtcdClient(endpoints, tlsConfig)
- if err != nil {
- return &Etcd{}, false, err
- }
- etc.Client = client
- etc.endpoints = endpoints
-
- return &etc, stubzones, nil
- }
- return &Etcd{}, false, nil
-}
-
-func newEtcdClient(endpoints []string, cc *tls.Config) (etcdc.KeysAPI, error) {
- etcdCfg := etcdc.Config{
- Endpoints: endpoints,
- Transport: mwtls.NewHTTPSTransport(cc),
- }
- cli, err := etcdc.New(etcdCfg)
- if err != nil {
- return nil, err
- }
- return etcdc.NewKeysAPI(cli), nil
-}
-
-const defaultEndpoint = "http://localhost:2379"
diff --git a/middleware/etcd/setup_test.go b/middleware/etcd/setup_test.go
deleted file mode 100644
index 833e2ba4c..000000000
--- a/middleware/etcd/setup_test.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package etcd
-
-import (
- "strings"
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestSetupEtcd(t *testing.T) {
- tests := []struct {
- input string
- shouldErr bool
- expectedPath string
- expectedEndpoint string
- expectedErrContent string // substring from the expected error. Empty for positive cases.
- }{
- // positive
- {
- `etcd`, false, "skydns", "http://localhost:2379", "",
- },
- {
- `etcd skydns.local {
- endpoint localhost:300
-}
-`, false, "skydns", "localhost:300", "",
- },
- // negative
- {
- `etcd {
- endpoints localhost:300
-}
-`, true, "", "", "unknown property 'endpoints'",
- },
- }
-
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- etcd, _ /*stubzones*/, err := etcdParse(c)
-
- if test.shouldErr && err == nil {
- t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input)
- }
-
- if err != nil {
- if !test.shouldErr {
- t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
- continue
- }
-
- if !strings.Contains(err.Error(), test.expectedErrContent) {
- t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input)
- continue
- }
- }
-
- if !test.shouldErr && etcd.PathPrefix != test.expectedPath {
- t.Errorf("Etcd not correctly set for input %s. Expected: %s, actual: %s", test.input, test.expectedPath, etcd.PathPrefix)
- }
- if !test.shouldErr && etcd.endpoints[0] != test.expectedEndpoint { // only checks the first
- t.Errorf("Etcd not correctly set for input %s. Expected: '%s', actual: '%s'", test.input, test.expectedEndpoint, etcd.endpoints[0])
- }
- }
-}
diff --git a/middleware/etcd/stub.go b/middleware/etcd/stub.go
deleted file mode 100644
index b40af6341..000000000
--- a/middleware/etcd/stub.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package etcd
-
-import (
- "log"
- "net"
- "strconv"
- "time"
-
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/middleware/proxy"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-// UpdateStubZones checks etcd for an update on the stubzones.
-func (e *Etcd) UpdateStubZones() {
- go func() {
- for {
- e.updateStubZones()
- time.Sleep(15 * time.Second)
- }
- }()
-}
-
-// Look in .../dns/stub/<zone>/xx for msg.Services. Loop through them
-// extract <zone> and add them as forwarders (ip:port-combos) for
-// the stub zones. Only numeric (i.e. IP address) hosts are used.
-// Only the first zone configured on e is used for the lookup.
-func (e *Etcd) updateStubZones() {
- zone := e.Zones[0]
-
- fakeState := request.Request{W: nil, Req: new(dns.Msg)}
- fakeState.Req.SetQuestion(stubDomain+"."+zone, dns.TypeA)
-
- services, err := e.Records(fakeState, false)
- if err != nil {
- return
- }
-
- stubmap := make(map[string]proxy.Proxy)
- // track the nameservers on a per domain basis, but allow a list on the domain.
- nameservers := map[string][]string{}
-
-Services:
- for _, serv := range services {
- if serv.Port == 0 {
- serv.Port = 53
- }
- ip := net.ParseIP(serv.Host)
- if ip == nil {
- log.Printf("[WARNING] Non IP address stub nameserver: %s", serv.Host)
- continue
- }
-
- domain := msg.Domain(serv.Key)
- labels := dns.SplitDomainName(domain)
-
- // If the remaining name equals any of the zones we have, we ignore it.
- for _, z := range e.Zones {
- // Chop of left most label, because that is used as the nameserver place holder
- // and drop the right most labels that belong to zone.
- // We must *also* chop of dns.stub. which means cutting two more labels.
- domain = dnsutil.Join(labels[1 : len(labels)-dns.CountLabel(z)-2])
- if domain == z {
- log.Printf("[WARNING] Skipping nameserver for domain we are authoritative for: %s", domain)
- continue Services
- }
- }
- nameservers[domain] = append(nameservers[domain], net.JoinHostPort(serv.Host, strconv.Itoa(serv.Port)))
- }
-
- for domain, nss := range nameservers {
- stubmap[domain] = proxy.NewLookup(nss)
- }
- // atomic swap (at least that's what we hope it is)
- if len(stubmap) > 0 {
- e.Stubmap = &stubmap
- }
- return
-}
diff --git a/middleware/etcd/stub_handler.go b/middleware/etcd/stub_handler.go
deleted file mode 100644
index a6b752177..000000000
--- a/middleware/etcd/stub_handler.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package etcd
-
-import (
- "errors"
- "log"
-
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// Stub wraps an Etcd. We have this type so that it can have a ServeDNS method.
-type Stub struct {
- *Etcd
- Zone string // for what zone (and thus what nameservers are we called)
-}
-
-// ServeDNS implements the middleware.Handler interface.
-func (s Stub) ServeDNS(ctx context.Context, w dns.ResponseWriter, req *dns.Msg) (int, error) {
- if hasStubEdns0(req) {
- log.Printf("[WARNING] Forwarding cycle detected, refusing msg: %s", req.Question[0].Name)
- return dns.RcodeRefused, errors.New("stub forward cycle")
- }
- req = addStubEdns0(req)
- proxy, ok := (*s.Etcd.Stubmap)[s.Zone]
- if !ok { // somebody made a mistake..
- return dns.RcodeServerFailure, nil
- }
-
- state := request.Request{W: w, Req: req}
- m, e := proxy.Forward(state)
- if e != nil {
- return dns.RcodeServerFailure, e
- }
- m.RecursionAvailable, m.Compress = true, true
- state.SizeAndDo(m)
- w.WriteMsg(m)
- return dns.RcodeSuccess, nil
-}
-
-// hasStubEdns0 checks if the message is carrying our special edns0 zero option.
-func hasStubEdns0(m *dns.Msg) bool {
- option := m.IsEdns0()
- if option == nil {
- return false
- }
- for _, o := range option.Option {
- if o.Option() == ednsStubCode && len(o.(*dns.EDNS0_LOCAL).Data) == 1 &&
- o.(*dns.EDNS0_LOCAL).Data[0] == 1 {
- return true
- }
- }
- return false
-}
-
-// addStubEdns0 adds our special option to the message's OPT record.
-func addStubEdns0(m *dns.Msg) *dns.Msg {
- option := m.IsEdns0()
- // Add a custom EDNS0 option to the packet, so we can detect loops when 2 stubs are forwarding to each other.
- if option != nil {
- option.Option = append(option.Option, &dns.EDNS0_LOCAL{Code: ednsStubCode, Data: []byte{1}})
- return m
- }
-
- m.Extra = append(m.Extra, ednsStub)
- return m
-}
-
-const (
- ednsStubCode = dns.EDNS0LOCALSTART + 10
- stubDomain = "stub.dns"
-)
-
-var ednsStub = func() *dns.OPT {
- o := new(dns.OPT)
- o.Hdr.Name = "."
- o.Hdr.Rrtype = dns.TypeOPT
- o.SetUDPSize(4096)
-
- e := new(dns.EDNS0_LOCAL)
- e.Code = ednsStubCode
- e.Data = []byte{1}
- o.Option = append(o.Option, e)
- return o
-}()
diff --git a/middleware/etcd/stub_test.go b/middleware/etcd/stub_test.go
deleted file mode 100644
index 5f22f42c9..000000000
--- a/middleware/etcd/stub_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// +build etcd
-
-package etcd
-
-import (
- "net"
- "strconv"
- "testing"
-
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
-)
-
-func fakeStubServerExampleNet(t *testing.T) (*dns.Server, string) {
- server, addr, err := test.UDPServer("127.0.0.1:0")
- if err != nil {
- t.Fatalf("failed to create a UDP server: %s", err)
- }
- // add handler for example.net
- dns.HandleFunc("example.net.", func(w dns.ResponseWriter, r *dns.Msg) {
- m := new(dns.Msg)
- m.SetReply(r)
- m.Answer = []dns.RR{test.A("example.net. 86400 IN A 93.184.216.34")}
- w.WriteMsg(m)
- })
-
- return server, addr
-}
-
-func TestStubLookup(t *testing.T) {
- server, addr := fakeStubServerExampleNet(t)
- defer server.Shutdown()
-
- host, p, _ := net.SplitHostPort(addr)
- port, _ := strconv.Atoi(p)
- exampleNetStub := &msg.Service{Host: host, Port: port, Key: "a.example.net.stub.dns.skydns.test."}
- servicesStub = append(servicesStub, exampleNetStub)
-
- etc := newEtcdMiddleware()
-
- for _, serv := range servicesStub {
- set(t, etc, serv.Key, 0, serv)
- defer delete(t, etc, serv.Key)
- }
-
- etc.updateStubZones()
-
- for _, tc := range dnsTestCasesStub {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := etc.ServeDNS(ctxt, rec, m)
- if err != nil && m.Question[0].Name == "example.org." {
- // This is OK, we expect this backend to *not* work.
- continue
- }
- if err != nil {
- t.Errorf("expected no error, got %v for %s\n", err, m.Question[0].Name)
- }
- resp := rec.Msg
- if resp == nil {
- // etcd not running?
- continue
- }
-
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-var servicesStub = []*msg.Service{
- // Two tests, ask a question that should return servfail because remote it no accessible
- // and one with edns0 option added, that should return refused.
- {Host: "127.0.0.1", Port: 666, Key: "b.example.org.stub.dns.skydns.test."},
-}
-
-var dnsTestCasesStub = []test.Case{
- {
- Qname: "example.org.", Qtype: dns.TypeA, Rcode: dns.RcodeServerFailure,
- },
- {
- Qname: "example.net.", Qtype: dns.TypeA,
- Answer: []dns.RR{test.A("example.net. 86400 IN A 93.184.216.34")},
- Extra: []dns.RR{test.OPT(4096, false)}, // This will have an EDNS0 section, because *we* added our local stub forward to detect loops.
- },
-}
diff --git a/middleware/federation/README.md b/middleware/federation/README.md
deleted file mode 100644
index c22a909f5..000000000
--- a/middleware/federation/README.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# federation
-
-The *federation* middleware enables
-[federated](https://kubernetes.io/docs/tasks/federation/federation-service-discovery/) queries to be
-resolved via the kubernetes middleware.
-
-Enabling *federation* without also having *kubernetes* is a noop.
-
-## Syntax
-
-~~~
-federation [ZONES...] {
- NAME DOMAIN
-~~~
-
-* Each **NAME** and **DOMAIN** defines federation membership. One entry for each. A duplicate
- **NAME** will silently overwrite any previous value.
-
-## Examples
-
-Here we handle all service requests in the `prod` and `stage` federations.
-
-~~~ txt
-. {
- kubernetes cluster.local
- federation cluster.local {
- prod prod.feddomain.com
- staging staging.feddomain.com
- }
-}
-~~~
-
-Or slightly shorter:
-
-~~~ txt
-cluster.local {
- kubernetes
- federation {
- prod prod.feddomain.com
- staging staging.feddomain.com
- }
-}
-~~~
diff --git a/middleware/federation/federation.go b/middleware/federation/federation.go
deleted file mode 100644
index bc74d9fc9..000000000
--- a/middleware/federation/federation.go
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
-Package federation implements kubernetes federation. It checks if the qname matches
-a possible federation. If this is the case and the captured answer is an NXDOMAIN,
-federation is performed. If this is not the case the original answer is returned.
-
-The federation label is always the 2nd to last once the zone is chopped of. For
-instance "nginx.mynamespace.myfederation.svc.example.com" has "myfederation" as
-the federation label. For federation to work we do a normal k8s lookup
-*without* that label, if that comes back with NXDOMAIN or NODATA(??) we create
-a federation record and return that.
-
-Federation is only useful in conjunction with the kubernetes middleware, without it is a noop.
-*/
-package federation
-
-import (
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/middleware/pkg/nonwriter"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// Federation contains the name to zone mapping used for federation in kubernetes.
-type Federation struct {
- f map[string]string
- zones []string
-
- Next middleware.Handler
- Federations Func
-}
-
-// Func needs to be implemented by any middleware that implements
-// federation. Right now this is only the kubernetes middleware.
-type Func func(state request.Request, fname, fzone string) (msg.Service, error)
-
-// New returns a new federation.
-func New() *Federation {
- return &Federation{f: make(map[string]string)}
-}
-
-// ServeDNS implements the middleware.Handle interface.
-func (f *Federation) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- if f.Federations == nil {
- return middleware.NextOrFailure(f.Name(), f.Next, ctx, w, r)
- }
-
- state := request.Request{W: w, Req: r}
- zone := middleware.Zones(f.zones).Matches(state.Name())
- if zone == "" {
- return middleware.NextOrFailure(f.Name(), f.Next, ctx, w, r)
- }
-
- state.Zone = zone
-
- // Remove the federation label from the qname to see if something exists.
- without, label := f.isNameFederation(state.Name(), state.Zone)
- if without == "" {
- return middleware.NextOrFailure(f.Name(), f.Next, ctx, w, r)
- }
-
- qname := r.Question[0].Name
- r.Question[0].Name = without
- state.Clear()
-
- // Start the next middleware, but with a nowriter, capture the result, if NXDOMAIN
- // perform federation, otherwise just write the result.
- nw := nonwriter.New(w)
- ret, err := middleware.NextOrFailure(f.Name(), f.Next, ctx, nw, r)
-
- if !middleware.ClientWrite(ret) {
- // something went wrong
- r.Question[0].Name = qname
- return ret, err
- }
-
- if m := nw.Msg; m.Rcode != dns.RcodeNameError {
- // If positive answer we need to substitute the original qname in the answer.
- m.Question[0].Name = qname
- for _, a := range m.Answer {
- a.Header().Name = qname
- }
-
- state.SizeAndDo(m)
- m, _ = state.Scrub(m)
- w.WriteMsg(m)
-
- return dns.RcodeSuccess, nil
- }
-
- // Still here, we've seen NXDOMAIN and need to perform federation.
- service, err := f.Federations(state, label, f.f[label]) // state references Req which has updated qname
- if err != nil {
- r.Question[0].Name = qname
- return dns.RcodeServerFailure, err
- }
-
- r.Question[0].Name = qname
-
- m := new(dns.Msg)
- m.SetReply(r)
- m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
-
- m.Answer = []dns.RR{service.NewCNAME(state.QName(), service.Host)}
-
- state.SizeAndDo(m)
- m, _ = state.Scrub(m)
- w.WriteMsg(m)
-
- return dns.RcodeSuccess, nil
-}
-
-// Name implements the middleware.Handle interface.
-func (f *Federation) Name() string { return "federation" }
-
-// IsNameFederation checks the qname to see if it is a potential federation. The federation
-// label is always the 2nd to last once the zone is chopped of. For instance
-// "nginx.mynamespace.myfederation.svc.example.com" has "myfederation" as the federation label.
-// IsNameFederation returns a new qname with the federation label and the label itself or two
-// empty strings if there wasn't a hit.
-func (f *Federation) isNameFederation(name, zone string) (string, string) {
- base, _ := dnsutil.TrimZone(name, zone)
-
- // TODO(miek): dns.PrevLabel is better for memory, or dns.Split.
- labels := dns.SplitDomainName(base)
- ll := len(labels)
- if ll < 2 {
- return "", ""
- }
-
- fed := labels[ll-2]
-
- if _, ok := f.f[fed]; ok {
- without := dnsutil.Join(labels[:ll-2]) + labels[ll-1] + "." + zone
- return without, fed
- }
- return "", ""
-}
diff --git a/middleware/federation/federation_test.go b/middleware/federation/federation_test.go
deleted file mode 100644
index 6f27cabad..000000000
--- a/middleware/federation/federation_test.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package federation
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/kubernetes"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func TestIsNameFederation(t *testing.T) {
- tests := []struct {
- fed string
- qname string
- expectedZone string
- }{
- {"prod", "nginx.mynamespace.prod.svc.example.com.", "nginx.mynamespace.svc.example.com."},
- {"prod", "nginx.mynamespace.staging.svc.example.com.", ""},
- {"prod", "nginx.mynamespace.example.com.", ""},
- {"prod", "example.com.", ""},
- {"prod", "com.", ""},
- }
-
- fed := New()
- for i, tc := range tests {
- fed.f[tc.fed] = "test-name"
- if x, _ := fed.isNameFederation(tc.qname, "example.com."); x != tc.expectedZone {
- t.Errorf("Test %d, failed to get zone, expected %s, got %s", i, tc.expectedZone, x)
- }
- }
-}
-
-func TestFederationKubernetes(t *testing.T) {
- tests := []test.Case{
- {
- // service exists so we return the IP address associated with it.
- Qname: "svc1.testns.prod.svc.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.A("svc1.testns.prod.svc.cluster.local. 303 IN A 10.0.0.1"),
- },
- },
- {
- // service does not exist, do the federation dance.
- Qname: "svc0.testns.prod.svc.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.CNAME("svc0.testns.prod.svc.cluster.local. 303 IN CNAME svc0.testns.prod.svc.fd-az.fd-r.federal.example."),
- },
- },
- }
-
- k := kubernetes.New([]string{"cluster.local."})
- k.APIConn = &APIConnFederationTest{}
-
- fed := New()
- fed.zones = []string{"cluster.local."}
- fed.Federations = k.Federations
- fed.Next = k
- fed.f = map[string]string{
- "prod": "federal.example.",
- }
-
- ctx := context.TODO()
- for i, tc := range tests {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fed.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Test %d, expected no error, got %v\n", i, err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
diff --git a/middleware/federation/kubernetes_api_test.go b/middleware/federation/kubernetes_api_test.go
deleted file mode 100644
index 9e7056e49..000000000
--- a/middleware/federation/kubernetes_api_test.go
+++ /dev/null
@@ -1,111 +0,0 @@
-package federation
-
-import (
- "github.com/coredns/coredns/middleware/kubernetes"
-
- "k8s.io/client-go/1.5/pkg/api"
-)
-
-type APIConnFederationTest struct{}
-
-func (APIConnFederationTest) Run() { return }
-func (APIConnFederationTest) Stop() error { return nil }
-
-func (APIConnFederationTest) PodIndex(string) []interface{} {
- a := make([]interface{}, 1)
- a[0] = &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Namespace: "podns",
- },
- Status: api.PodStatus{
- PodIP: "10.240.0.1", // Remote IP set in test.ResponseWriter
- },
- }
- return a
-}
-
-func (APIConnFederationTest) ServiceList() []*api.Service {
- svcs := []*api.Service{
- {
- ObjectMeta: api.ObjectMeta{
- Name: "svc1",
- Namespace: "testns",
- },
- Spec: api.ServiceSpec{
- ClusterIP: "10.0.0.1",
- Ports: []api.ServicePort{{
- Name: "http",
- Protocol: "tcp",
- Port: 80,
- }},
- },
- },
- {
- ObjectMeta: api.ObjectMeta{
- Name: "hdls1",
- Namespace: "testns",
- },
- Spec: api.ServiceSpec{
- ClusterIP: api.ClusterIPNone,
- },
- },
- {
- ObjectMeta: api.ObjectMeta{
- Name: "external",
- Namespace: "testns",
- },
- Spec: api.ServiceSpec{
- ExternalName: "ext.interwebs.test",
- Ports: []api.ServicePort{{
- Name: "http",
- Protocol: "tcp",
- Port: 80,
- }},
- },
- },
- }
- return svcs
-
-}
-
-func (APIConnFederationTest) EndpointsList() api.EndpointsList {
- return api.EndpointsList{
- Items: []api.Endpoints{
- {
- Subsets: []api.EndpointSubset{
- {
- Addresses: []api.EndpointAddress{
- {
- IP: "172.0.0.1",
- Hostname: "ep1a",
- },
- },
- Ports: []api.EndpointPort{
- {
- Port: 80,
- Protocol: "tcp",
- Name: "http",
- },
- },
- },
- },
- ObjectMeta: api.ObjectMeta{
- Name: "svc1",
- Namespace: "testns",
- },
- },
- },
- }
-}
-
-func (APIConnFederationTest) GetNodeByName(name string) (api.Node, error) {
- return api.Node{
- ObjectMeta: api.ObjectMeta{
- Name: "test.node.foo.bar",
- Labels: map[string]string{
- kubernetes.LabelRegion: "fd-r",
- kubernetes.LabelZone: "fd-az",
- },
- },
- }, nil
-}
diff --git a/middleware/federation/setup.go b/middleware/federation/setup.go
deleted file mode 100644
index 1ef635603..000000000
--- a/middleware/federation/setup.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package federation
-
-import (
- "fmt"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/kubernetes"
- "github.com/miekg/dns"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("federation", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- fed, err := federationParse(c)
- if err != nil {
- return middleware.Error("federation", err)
- }
-
- // Do this in OnStartup, so all middleware has been initialized.
- c.OnStartup(func() error {
- m := dnsserver.GetConfig(c).Handler("kubernetes")
- if m == nil {
- return nil
- }
- if x, ok := m.(*kubernetes.Kubernetes); ok {
- fed.Federations = x.Federations
- }
- return nil
- })
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- fed.Next = next
- return fed
- })
-
- return nil
-}
-
-func federationParse(c *caddy.Controller) (*Federation, error) {
- fed := New()
-
- for c.Next() {
- // federation [zones..]
- zones := c.RemainingArgs()
- origins := []string{}
- if len(zones) > 0 {
- origins = make([]string, len(zones))
- copy(origins, zones)
- } else {
- origins = make([]string, len(c.ServerBlockKeys))
- copy(origins, c.ServerBlockKeys)
- }
-
- for c.NextBlock() {
- x := c.Val()
- switch x {
- default:
- args := c.RemainingArgs()
- if x := len(args); x != 1 {
- return fed, fmt.Errorf("need two arguments for federation, got %d", x)
- }
-
- fed.f[x] = dns.Fqdn(args[0])
- }
- }
-
- for i := range origins {
- origins[i] = middleware.Host(origins[i]).Normalize()
- }
-
- fed.zones = origins
-
- if len(fed.f) == 0 {
- return fed, fmt.Errorf("at least one name to zone federation expected")
- }
-
- return fed, nil
- }
-
- return fed, nil
-}
diff --git a/middleware/federation/setup_test.go b/middleware/federation/setup_test.go
deleted file mode 100644
index e85b01772..000000000
--- a/middleware/federation/setup_test.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package federation
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestSetup(t *testing.T) {
- tests := []struct {
- input string
- shouldErr bool
- expectedLen int
- expectedNameZone []string // contains only entry for now
- }{
- // ok
- {`federation {
- prod prod.example.org
- }`, false, 1, []string{"prod", "prod.example.org."}},
-
- {`federation {
- staging staging.example.org
- prod prod.example.org
- }`, false, 2, []string{"prod", "prod.example.org."}},
- {`federation {
- staging staging.example.org
- prod prod.example.org
- }`, false, 2, []string{"staging", "staging.example.org."}},
- {`federation example.com {
- staging staging.example.org
- prod prod.example.org
- }`, false, 2, []string{"staging", "staging.example.org."}},
- // errors
- {`federation {
- }`, true, 0, []string{}},
- {`federation {
- staging
- }`, true, 0, []string{}},
- }
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- fed, err := federationParse(c)
- if test.shouldErr && err == nil {
- t.Errorf("Test %v: Expected error but found nil", i)
- continue
- } else if !test.shouldErr && err != nil {
- t.Errorf("Test %v: Expected no error but found error: %v", i, err)
- continue
- }
- if test.shouldErr && err != nil {
- continue
- }
-
- if x := len(fed.f); x != test.expectedLen {
- t.Errorf("Test %v: Expected map length of %d, got: %d", i, test.expectedLen, x)
- }
- if x, ok := fed.f[test.expectedNameZone[0]]; !ok {
- t.Errorf("Test %v: Expected name for %s, got nothing", i, test.expectedNameZone[0])
- } else {
- if x != test.expectedNameZone[1] {
- t.Errorf("Test %v: Expected zone: %s, got %s", i, test.expectedNameZone[1], x)
- }
- }
- }
-}
diff --git a/middleware/file/README.md b/middleware/file/README.md
deleted file mode 100644
index 29656d161..000000000
--- a/middleware/file/README.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# file
-
-*file* enables serving zone data from an RFC 1035-style master file.
-
-The file middleware is used for an "old-style" DNS server. It serves from a preloaded file that exists
-on disk. If the zone file contains signatures (i.e. is signed, i.e. DNSSEC) correct DNSSEC answers
-are returned. Only NSEC is supported! If you use this setup *you* are responsible for resigning the
-zonefile.
-
-## Syntax
-
-~~~
-file DBFILE [ZONES...]
-~~~
-
-* **DBFILE** the database file to read and parse. If the path is relative the path from the *root*
- directive will be prepended to it.
-* **ZONES** zones it should be authoritative for. If empty, the zones from the configuration block
- are used.
-
-If you want to round robin A and AAAA responses look at the *loadbalance* middleware.
-
-TSIG key configuration is TODO; directive format for transfer will probably be extended with
-TSIG key information, something like `transfer out [ADDRESS...] key [NAME[:ALG]] [BASE64]`
-
-~~~
-file DBFILE [ZONES... ] {
- transfer to ADDRESS...
- no_reload
- upstream ADDRESS...
-}
-~~~
-
-* `transfer` enables zone transfers. It may be specified multiples times. `To` or `from` signals
- the direction. **ADDRESS** must be denoted in CIDR notation (127.0.0.1/32 etc.) or just as plain
- addresses. The special wildcard `*` means: the entire internet (only valid for 'transfer to').
- When an address is specified a notify message will be send whenever the zone is reloaded.
-* `no_reload` by default CoreDNS will reload a zone from disk whenever it detects a change to the
- file. This option disables that behavior.
-* `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs)
- pointing to external names. This is only really useful when CoreDNS is configured as a proxy, for
- normal authoritative serving you don't need *or* want to use this. **ADDRESS** can be an IP
- address, and IP:port or a string pointing to a file that is structured as /etc/resolv.conf.
-
-## Examples
-
-Load the `example.org` zone from `example.org.signed` and allow transfers to the internet, but send
-notifies to 10.240.1.1
-
-~~~
-file example.org.signed example.org {
- transfer to *
- transfer to 10.240.1.1
-}
-~~~
diff --git a/middleware/file/closest.go b/middleware/file/closest.go
deleted file mode 100644
index ea201671f..000000000
--- a/middleware/file/closest.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package file
-
-import (
- "github.com/coredns/coredns/middleware/file/tree"
-
- "github.com/miekg/dns"
-)
-
-// ClosestEncloser returns the closest encloser for qname.
-func (z *Zone) ClosestEncloser(qname string) (*tree.Elem, bool) {
-
- offset, end := dns.NextLabel(qname, 0)
- for !end {
- elem, _ := z.Tree.Search(qname)
- if elem != nil {
- return elem, true
- }
- qname = qname[offset:]
-
- offset, end = dns.NextLabel(qname, offset)
- }
-
- return z.Tree.Search(z.origin)
-}
diff --git a/middleware/file/closest_test.go b/middleware/file/closest_test.go
deleted file mode 100644
index b37495493..000000000
--- a/middleware/file/closest_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package file
-
-import (
- "strings"
- "testing"
-)
-
-func TestClosestEncloser(t *testing.T) {
- z, err := Parse(strings.NewReader(dbMiekNL), testzone, "stdin", 0)
- if err != nil {
- t.Fatalf("expect no error when reading zone, got %q", err)
- }
-
- tests := []struct {
- in, out string
- }{
- {"miek.nl.", "miek.nl."},
- {"www.miek.nl.", "www.miek.nl."},
-
- {"blaat.miek.nl.", "miek.nl."},
- {"blaat.www.miek.nl.", "www.miek.nl."},
- {"www.blaat.miek.nl.", "miek.nl."},
- {"blaat.a.miek.nl.", "a.miek.nl."},
- }
-
- for _, tc := range tests {
- ce, _ := z.ClosestEncloser(tc.in)
- if ce == nil {
- if z.origin != tc.out {
- t.Errorf("Expected ce to be %s for %s, got %s", tc.out, tc.in, ce.Name())
- }
- continue
- }
- if ce.Name() != tc.out {
- t.Errorf("Expected ce to be %s for %s, got %s", tc.out, tc.in, ce.Name())
- }
- }
-}
diff --git a/middleware/file/cname_test.go b/middleware/file/cname_test.go
deleted file mode 100644
index 90e6f05a9..000000000
--- a/middleware/file/cname_test.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package file
-
-import (
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/proxy"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func TestLookupCNAMEChain(t *testing.T) {
- name := "example.org."
- zone, err := Parse(strings.NewReader(dbExampleCNAME), name, "stdin", 0)
- if err != nil {
- t.Fatalf("Expected no error when reading zone, got %q", err)
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{name: zone}, Names: []string{name}}}
- ctx := context.TODO()
-
- for _, tc := range cnameTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-var cnameTestCases = []test.Case{
- {
- Qname: "a.example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.A("a.example.org. 1800 IN A 127.0.0.1"),
- },
- },
- {
- Qname: "www3.example.org.", Qtype: dns.TypeCNAME,
- Answer: []dns.RR{
- test.CNAME("www3.example.org. 1800 IN CNAME www2.example.org."),
- },
- },
- {
- Qname: "dangling.example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.CNAME("dangling.example.org. 1800 IN CNAME foo.example.org."),
- },
- },
- {
- Qname: "www3.example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.A("a.example.org. 1800 IN A 127.0.0.1"),
- test.CNAME("www.example.org. 1800 IN CNAME a.example.org."),
- test.CNAME("www1.example.org. 1800 IN CNAME www.example.org."),
- test.CNAME("www2.example.org. 1800 IN CNAME www1.example.org."),
- test.CNAME("www3.example.org. 1800 IN CNAME www2.example.org."),
- },
- },
-}
-
-func TestLookupCNAMEExternal(t *testing.T) {
- name := "example.org."
- zone, err := Parse(strings.NewReader(dbExampleCNAME), name, "stdin", 0)
- if err != nil {
- t.Fatalf("Expected no error when reading zone, got %q", err)
- }
- zone.Proxy = proxy.NewLookup([]string{"8.8.8.8:53"}) // TODO(miek): point to local instance
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{name: zone}, Names: []string{name}}}
- ctx := context.TODO()
-
- for _, tc := range exernalTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-var exernalTestCases = []test.Case{
- {
- Qname: "external.example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.CNAME("external.example.org. 1800 CNAME www.example.net."),
- // magic 303 TTL that says: don't check TTL.
- test.A("www.example.net. 303 IN A 93.184.216.34"),
- },
- },
-}
-
-const dbExampleCNAME = `
-$TTL 30M
-$ORIGIN example.org.
-@ IN SOA linode.atoom.net. miek.miek.nl. (
- 1282630057 ; Serial
- 4H ; Refresh
- 1H ; Retry
- 7D ; Expire
- 4H ) ; Negative Cache TTL
-
-a IN A 127.0.0.1
-www3 IN CNAME www2
-www2 IN CNAME www1
-www1 IN CNAME www
-www IN CNAME a
-dangling IN CNAME foo
-external IN CNAME www.example.net.`
diff --git a/middleware/file/delegation_test.go b/middleware/file/delegation_test.go
deleted file mode 100644
index 49510944f..000000000
--- a/middleware/file/delegation_test.go
+++ /dev/null
@@ -1,207 +0,0 @@
-package file
-
-import (
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-var delegationTestCases = []test.Case{
- {
- Qname: "a.delegated.miek.nl.", Qtype: dns.TypeTXT,
- Ns: []dns.RR{
- test.NS("delegated.miek.nl. 1800 IN NS a.delegated.miek.nl."),
- test.NS("delegated.miek.nl. 1800 IN NS ns-ext.nlnetlabs.nl."),
- },
- Extra: []dns.RR{
- test.A("a.delegated.miek.nl. 1800 IN A 139.162.196.78"),
- test.AAAA("a.delegated.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- },
- {
- Qname: "delegated.miek.nl.", Qtype: dns.TypeNS,
- Answer: []dns.RR{
- test.NS("delegated.miek.nl. 1800 IN NS a.delegated.miek.nl."),
- test.NS("delegated.miek.nl. 1800 IN NS ns-ext.nlnetlabs.nl."),
- },
- Extra: []dns.RR{
- test.A("a.delegated.miek.nl. 1800 IN A 139.162.196.78"),
- test.AAAA("a.delegated.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- },
- {
- Qname: "foo.delegated.miek.nl.", Qtype: dns.TypeA,
- Ns: []dns.RR{
- test.NS("delegated.miek.nl. 1800 IN NS a.delegated.miek.nl."),
- test.NS("delegated.miek.nl. 1800 IN NS ns-ext.nlnetlabs.nl."),
- },
- Extra: []dns.RR{
- test.A("a.delegated.miek.nl. 1800 IN A 139.162.196.78"),
- test.AAAA("a.delegated.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- },
- {
- Qname: "foo.delegated.miek.nl.", Qtype: dns.TypeTXT,
- Ns: []dns.RR{
- test.NS("delegated.miek.nl. 1800 IN NS a.delegated.miek.nl."),
- test.NS("delegated.miek.nl. 1800 IN NS ns-ext.nlnetlabs.nl."),
- },
- Extra: []dns.RR{
- test.A("a.delegated.miek.nl. 1800 IN A 139.162.196.78"),
- test.AAAA("a.delegated.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- },
- {
- Qname: "miek.nl.", Qtype: dns.TypeSOA,
- Answer: []dns.RR{
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- Ns: miekAuth,
- },
- {
- Qname: "miek.nl.", Qtype: dns.TypeAAAA,
- Ns: []dns.RR{
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- },
-}
-
-var secureDelegationTestCases = []test.Case{
- {
- Qname: "a.delegated.example.org.", Qtype: dns.TypeTXT,
- Do: true,
- Ns: []dns.RR{
- test.DS("delegated.example.org. 1800 IN DS 10056 5 1 EE72CABD1927759CDDA92A10DBF431504B9E1F13"),
- test.DS("delegated.example.org. 1800 IN DS 10056 5 2 E4B05F87725FA86D9A64F1E53C3D0E6250946599DFE639C45955B0ED416CDDFA"),
- test.NS("delegated.example.org. 1800 IN NS a.delegated.example.org."),
- test.NS("delegated.example.org. 1800 IN NS ns-ext.nlnetlabs.nl."),
- test.RRSIG("delegated.example.org. 1800 IN RRSIG DS 13 3 1800 20161129153240 20161030153240 49035 example.org. rlNNzcUmtbjLSl02ZzQGUbWX75yCUx0Mug1jHtKVqRq1hpPE2S3863tIWSlz+W9wz4o19OI4jbznKKqk+DGKog=="),
- },
- Extra: []dns.RR{
- test.OPT(4096, true),
- test.A("a.delegated.example.org. 1800 IN A 139.162.196.78"),
- test.AAAA("a.delegated.example.org. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- },
- {
- Qname: "delegated.example.org.", Qtype: dns.TypeNS,
- Do: true,
- Answer: []dns.RR{
- test.NS("delegated.example.org. 1800 IN NS a.delegated.example.org."),
- test.NS("delegated.example.org. 1800 IN NS ns-ext.nlnetlabs.nl."),
- },
- Extra: []dns.RR{
- test.OPT(4096, true),
- test.A("a.delegated.example.org. 1800 IN A 139.162.196.78"),
- test.AAAA("a.delegated.example.org. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- },
- {
- Qname: "foo.delegated.example.org.", Qtype: dns.TypeA,
- Do: true,
- Ns: []dns.RR{
- test.DS("delegated.example.org. 1800 IN DS 10056 5 1 EE72CABD1927759CDDA92A10DBF431504B9E1F13"),
- test.DS("delegated.example.org. 1800 IN DS 10056 5 2 E4B05F87725FA86D9A64F1E53C3D0E6250946599DFE639C45955B0ED416CDDFA"),
- test.NS("delegated.example.org. 1800 IN NS a.delegated.example.org."),
- test.NS("delegated.example.org. 1800 IN NS ns-ext.nlnetlabs.nl."),
- test.RRSIG("delegated.example.org. 1800 IN RRSIG DS 13 3 1800 20161129153240 20161030153240 49035 example.org. rlNNzcUmtbjLSl02ZzQGUbWX75yCUx0Mug1jHtKVqRq1hpPE2S3863tIWSlz+W9wz4o19OI4jbznKKqk+DGKog=="),
- },
- Extra: []dns.RR{
- test.OPT(4096, true),
- test.A("a.delegated.example.org. 1800 IN A 139.162.196.78"),
- test.AAAA("a.delegated.example.org. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- },
- {
- Qname: "foo.delegated.example.org.", Qtype: dns.TypeTXT,
- Do: true,
- Ns: []dns.RR{
- test.DS("delegated.example.org. 1800 IN DS 10056 5 1 EE72CABD1927759CDDA92A10DBF431504B9E1F13"),
- test.DS("delegated.example.org. 1800 IN DS 10056 5 2 E4B05F87725FA86D9A64F1E53C3D0E6250946599DFE639C45955B0ED416CDDFA"),
- test.NS("delegated.example.org. 1800 IN NS a.delegated.example.org."),
- test.NS("delegated.example.org. 1800 IN NS ns-ext.nlnetlabs.nl."),
- test.RRSIG("delegated.example.org. 1800 IN RRSIG DS 13 3 1800 20161129153240 20161030153240 49035 example.org. rlNNzcUmtbjLSl02ZzQGUbWX75yCUx0Mug1jHtKVqRq1hpPE2S3863tIWSlz+W9wz4o19OI4jbznKKqk+DGKog=="),
- },
- Extra: []dns.RR{
- test.OPT(4096, true),
- test.A("a.delegated.example.org. 1800 IN A 139.162.196.78"),
- test.AAAA("a.delegated.example.org. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- },
-}
-
-var miekAuth = []dns.RR{
- test.NS("miek.nl. 1800 IN NS ext.ns.whyscream.net."),
- test.NS("miek.nl. 1800 IN NS linode.atoom.net."),
- test.NS("miek.nl. 1800 IN NS ns-ext.nlnetlabs.nl."),
- test.NS("miek.nl. 1800 IN NS omval.tednet.nl."),
-}
-
-func TestLookupDelegation(t *testing.T) {
- testDelegation(t, dbMiekNLDelegation, testzone, delegationTestCases)
-}
-
-func TestLookupSecureDelegation(t *testing.T) {
- testDelegation(t, exampleOrgSigned, "example.org.", secureDelegationTestCases)
-}
-
-func testDelegation(t *testing.T, z, origin string, testcases []test.Case) {
- zone, err := Parse(strings.NewReader(z), origin, "stdin", 0)
- if err != nil {
- t.Fatalf("Expect no error when reading zone, got %q", err)
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{origin: zone}, Names: []string{origin}}}
- ctx := context.TODO()
-
- for _, tc := range testcases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Expected no error, got %q\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-const dbMiekNLDelegation = `
-$TTL 30M
-$ORIGIN miek.nl.
-@ IN SOA linode.atoom.net. miek.miek.nl. (
- 1282630057 ; Serial
- 4H ; Refresh
- 1H ; Retry
- 7D ; Expire
- 4H ) ; Negative Cache TTL
- IN NS linode.atoom.net.
- IN NS ns-ext.nlnetlabs.nl.
- IN NS omval.tednet.nl.
- IN NS ext.ns.whyscream.net.
-
- IN MX 1 aspmx.l.google.com.
- IN MX 5 alt1.aspmx.l.google.com.
- IN MX 5 alt2.aspmx.l.google.com.
- IN MX 10 aspmx2.googlemail.com.
- IN MX 10 aspmx3.googlemail.com.
-
-delegated IN NS a.delegated
- IN NS ns-ext.nlnetlabs.nl.
-
-a.delegated IN TXT "obscured"
- IN A 139.162.196.78
- IN AAAA 2a01:7e00::f03c:91ff:fef1:6735
-
-a IN A 139.162.196.78
- IN AAAA 2a01:7e00::f03c:91ff:fef1:6735
-www IN CNAME a
-archive IN CNAME a`
diff --git a/middleware/file/dname.go b/middleware/file/dname.go
deleted file mode 100644
index 6bbfd9377..000000000
--- a/middleware/file/dname.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package file
-
-import (
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
-
- "github.com/miekg/dns"
-)
-
-// substituteDNAME performs the DNAME substitution defined by RFC 6672,
-// assuming the QTYPE of the query is not DNAME. It returns an empty
-// string if there is no match.
-func substituteDNAME(qname, owner, target string) string {
- if dns.IsSubDomain(owner, qname) && qname != owner {
- labels := dns.SplitDomainName(qname)
- labels = append(labels[0:len(labels)-dns.CountLabel(owner)], dns.SplitDomainName(target)...)
-
- return dnsutil.Join(labels)
- }
-
- return ""
-}
-
-// synthesizeCNAME returns a CNAME RR pointing to the resulting name of
-// the DNAME substitution. The owner name of the CNAME is the QNAME of
-// the query and the TTL is the same as the corresponding DNAME RR.
-//
-// It returns nil if the DNAME substitution has no match.
-func synthesizeCNAME(qname string, d *dns.DNAME) *dns.CNAME {
- target := substituteDNAME(qname, d.Header().Name, d.Target)
- if target == "" {
- return nil
- }
-
- r := new(dns.CNAME)
- r.Hdr = dns.RR_Header{
- Name: qname,
- Rrtype: dns.TypeCNAME,
- Class: dns.ClassINET,
- Ttl: d.Header().Ttl,
- }
- r.Target = target
-
- return r
-}
diff --git a/middleware/file/dname_test.go b/middleware/file/dname_test.go
deleted file mode 100644
index 27890a78c..000000000
--- a/middleware/file/dname_test.go
+++ /dev/null
@@ -1,300 +0,0 @@
-package file
-
-import (
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// RFC 6672, Section 2.2. Assuming QTYPE != DNAME.
-var dnameSubstitutionTestCases = []struct {
- qname string
- owner string
- target string
- expected string
-}{
- {"com.", "example.com.", "example.net.", ""},
- {"example.com.", "example.com.", "example.net.", ""},
- {"a.example.com.", "example.com.", "example.net.", "a.example.net."},
- {"a.b.example.com.", "example.com.", "example.net.", "a.b.example.net."},
- {"ab.example.com.", "b.example.com.", "example.net.", ""},
- {"foo.example.com.", "example.com.", "example.net.", "foo.example.net."},
- {"a.x.example.com.", "x.example.com.", "example.net.", "a.example.net."},
- {"a.example.com.", "example.com.", "y.example.net.", "a.y.example.net."},
- {"cyc.example.com.", "example.com.", "example.com.", "cyc.example.com."},
- {"cyc.example.com.", "example.com.", "c.example.com.", "cyc.c.example.com."},
- {"shortloop.x.x.", "x.", ".", "shortloop.x."},
- {"shortloop.x.", "x.", ".", "shortloop."},
-}
-
-func TestDNAMESubstitution(t *testing.T) {
- for i, tc := range dnameSubstitutionTestCases {
- result := substituteDNAME(tc.qname, tc.owner, tc.target)
- if result != tc.expected {
- if result == "" {
- result = "<no match>"
- }
-
- t.Errorf("Case %d: Expected %s -> %s, got %v", i, tc.qname, tc.expected, result)
- return
- }
- }
-}
-
-var dnameTestCases = []test.Case{
- {
- Qname: "dname.miek.nl.", Qtype: dns.TypeDNAME,
- Answer: []dns.RR{
- test.DNAME("dname.miek.nl. 1800 IN DNAME test.miek.nl."),
- },
- Ns: miekAuth,
- },
- {
- Qname: "dname.miek.nl.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.A("dname.miek.nl. 1800 IN A 127.0.0.1"),
- },
- Ns: miekAuth,
- },
- {
- Qname: "dname.miek.nl.", Qtype: dns.TypeMX,
- Answer: []dns.RR{},
- Ns: []dns.RR{
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- },
- {
- Qname: "a.dname.miek.nl.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.CNAME("a.dname.miek.nl. 1800 IN CNAME a.test.miek.nl."),
- test.A("a.test.miek.nl. 1800 IN A 139.162.196.78"),
- test.DNAME("dname.miek.nl. 1800 IN DNAME test.miek.nl."),
- },
- Ns: miekAuth,
- },
- {
- Qname: "www.dname.miek.nl.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.A("a.test.miek.nl. 1800 IN A 139.162.196.78"),
- test.DNAME("dname.miek.nl. 1800 IN DNAME test.miek.nl."),
- test.CNAME("www.dname.miek.nl. 1800 IN CNAME www.test.miek.nl."),
- test.CNAME("www.test.miek.nl. 1800 IN CNAME a.test.miek.nl."),
- },
- Ns: miekAuth,
- },
-}
-
-func TestLookupDNAME(t *testing.T) {
- zone, err := Parse(strings.NewReader(dbMiekNLDNAME), testzone, "stdin", 0)
- if err != nil {
- t.Fatalf("Expect no error when reading zone, got %q", err)
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{testzone: zone}, Names: []string{testzone}}}
- ctx := context.TODO()
-
- for _, tc := range dnameTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-var dnameDnssecTestCases = []test.Case{
- {
- // We have no auth section, because the test zone does not have nameservers.
- Qname: "ns.example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.A("ns.example.org. 1800 IN A 127.0.0.1"),
- },
- },
- {
- Qname: "dname.example.org.", Qtype: dns.TypeDNAME,
- Do: true,
- Answer: []dns.RR{
- test.DNAME("dname.example.org. 1800 IN DNAME test.example.org."),
- test.RRSIG("dname.example.org. 1800 IN RRSIG DNAME 5 3 1800 20170702091734 20170602091734 54282 example.org. HvXtiBM="),
- },
- Extra: []dns.RR{test.OPT(4096, true)},
- },
- {
- Qname: "a.dname.example.org.", Qtype: dns.TypeA,
- Do: true,
- Answer: []dns.RR{
- test.CNAME("a.dname.example.org. 1800 IN CNAME a.test.example.org."),
- test.DNAME("dname.example.org. 1800 IN DNAME test.example.org."),
- test.RRSIG("dname.example.org. 1800 IN RRSIG DNAME 5 3 1800 20170702091734 20170602091734 54282 example.org. HvXtiBM="),
- },
- Extra: []dns.RR{test.OPT(4096, true)},
- },
-}
-
-func TestLookupDNAMEDNSSEC(t *testing.T) {
- zone, err := Parse(strings.NewReader(dbExampleDNAMESigned), testzone, "stdin", 0)
- if err != nil {
- t.Fatalf("Expect no error when reading zone, got %q", err)
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{"example.org.": zone}, Names: []string{"example.org."}}}
- ctx := context.TODO()
-
- for _, tc := range dnameDnssecTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-const dbMiekNLDNAME = `
-$TTL 30M
-$ORIGIN miek.nl.
-@ IN SOA linode.atoom.net. miek.miek.nl. (
- 1282630057 ; Serial
- 4H ; Refresh
- 1H ; Retry
- 7D ; Expire
- 4H ) ; Negative Cache TTL
- IN NS linode.atoom.net.
- IN NS ns-ext.nlnetlabs.nl.
- IN NS omval.tednet.nl.
- IN NS ext.ns.whyscream.net.
-
-test IN MX 1 aspmx.l.google.com.
- IN MX 5 alt1.aspmx.l.google.com.
- IN MX 5 alt2.aspmx.l.google.com.
- IN MX 10 aspmx2.googlemail.com.
- IN MX 10 aspmx3.googlemail.com.
-a.test IN A 139.162.196.78
- IN AAAA 2a01:7e00::f03c:91ff:fef1:6735
-www.test IN CNAME a.test
-
-dname IN DNAME test
-dname IN A 127.0.0.1
-a.dname IN A 127.0.0.1
-`
-
-const dbExampleDNAMESigned = `
-; File written on Fri Jun 2 10:17:34 2017
-; dnssec_signzone version 9.10.3-P4-Debian
-example.org. 1800 IN SOA a.example.org. b.example.org. (
- 1282630057 ; serial
- 14400 ; refresh (4 hours)
- 3600 ; retry (1 hour)
- 604800 ; expire (1 week)
- 14400 ; minimum (4 hours)
- )
- 1800 RRSIG SOA 5 2 1800 (
- 20170702091734 20170602091734 54282 example.org.
- mr5eQtFs1GubgwaCcqrpiF6Cgi822OkESPeV
- X0OJYq3JzthJjHw8TfYAJWQ2yGqhlePHir9h
- FT/uFZdYyytHq+qgIUbJ9IVCrq0gZISZdHML
- Ry1DNffMR9CpD77KocOAUABfopcvH/3UGOHn
- TFxkAr447zPaaoC68JYGxYLfZk8= )
- 1800 NS ns.example.org.
- 1800 RRSIG NS 5 2 1800 (
- 20170702091734 20170602091734 54282 example.org.
- McM4UdMxkscVQkJnnEbdqwyjpPgq5a/EuOLA
- r2MvG43/cwOaWULiZoNzLi5Rjzhf+GTeVTan
- jw6EsL3gEuYI1nznwlLQ04/G0XAHjbq5VvJc
- rlscBD+dzf774yfaTjRNoeo2xTem6S7nyYPW
- Y+1f6xkrsQPLYJfZ6VZ9QqyupBw= )
- 14400 NSEC dname.example.org. NS SOA RRSIG NSEC DNSKEY
- 14400 RRSIG NSEC 5 2 14400 (
- 20170702091734 20170602091734 54282 example.org.
- VT+IbjDFajM0doMKFipdX3+UXfCn3iHIxg5x
- LElp4Q/YddTbX+6tZf53+EO+G8Kye3JDLwEl
- o8VceijNeF3igZ+LiZuXCei5Qg/TJ7IAUnAO
- xd85IWwEYwyKkKd6Z2kXbAN2pdcHE8EmboQd
- wfTr9oyWhpZk1Z+pN8vdejPrG0M= )
- 1800 DNSKEY 256 3 5 (
- AwEAAczLlmTk5bMXUzpBo/Jta6MWSZYy3Nfw
- gz8t/pkfSh4IlFF6vyXZhEqCeQsCBdD7ltkD
- h5qd4A+nFrYOMwsi5XIjoHMlJN15xwFS9EgS
- ZrZmuxePIEiYB5KccEf9JQMgM1t07Iu1FnrY
- 02OuAqGWcO4tuyTLaK3QP4MLQOfAgKqf
- ) ; ZSK; alg = RSASHA1; key id = 54282
- 1800 RRSIG DNSKEY 5 2 1800 (
- 20170702091734 20170602091734 54282 example.org.
- MBgSRtZ6idJblLIHxZWpWL/1oqIwImb1mkl7
- hDFxqV6Hw19yLX06P7gcJEWiisdZBkVEfcOK
- LeMJly05vgKfrMzLgIu2Ry4bL8AMKc8NMXBG
- b1VDCEBW69P2omogj2KnORHDCZQr/BX9+wBU
- 5rIMTTKlMSI5sT6ecJHHEymtiac= )
-dname.example.org. 1800 IN A 127.0.0.1
- 1800 RRSIG A 5 3 1800 (
- 20170702091734 20170602091734 54282 example.org.
- LPCK2nLyDdGwvmzGLkUO2atEUjoc+aEspkC3
- keZCdXZaLnAwBH7dNAjvvXzzy0WrgWeiyDb4
- +rJ2N0oaKEZicM4QQDHKhugJblKbU5G4qTey
- LSEaV3vvQnzGd0S6dCqnwfPj9czagFN7Zlf5
- DmLtdxx0aiDPCUpqT0+H/vuGPfk= )
- 1800 DNAME test.example.org.
- 1800 RRSIG DNAME 5 3 1800 (
- 20170702091734 20170602091734 54282 example.org.
- HvX79T1flWJ8H9/1XZjX6gz8rP/o2jbfPXJ9
- vC7ids/ZJilSReabLru4DCqcw1IV2DM/CZdE
- tBnED/T2PJXvMut9tnYMrz+ZFPxoV6XyA3Z7
- bok3B0OuxizzAN2EXdol04VdbMHoWUzjQCzi
- 0Ri12zLGRPzDepZ7FolgD+JtiBM= )
- 14400 NSEC a.dname.example.org. A DNAME RRSIG NSEC
- 14400 RRSIG NSEC 5 3 14400 (
- 20170702091734 20170602091734 54282 example.org.
- U3ZPYMUBJl3wF2SazQv/kBf6ec0CH+7n0Hr9
- w6lBKkiXz7P9WQzJDVnTHEZOrbDI6UetFGyC
- 6qcaADCASZ9Wxc+riyK1Hl4ox+Y/CHJ97WHy
- oS2X//vEf6qmbHQXin0WQtFdU/VCRYF40X5v
- 8VfqOmrr8iKiEqXND8XNVf58mTw= )
-a.dname.example.org. 1800 IN A 127.0.0.1
- 1800 RRSIG A 5 4 1800 (
- 20170702091734 20170602091734 54282 example.org.
- y7RHBWZwli8SJQ4BgTmdXmYS3KGHZ7AitJCx
- zXFksMQtNoOfVEQBwnFqjAb8ezcV5u92h1gN
- i1EcuxCFiElML1XFT8dK2GnlPAga9w3oIwd5
- wzW/YHcnR0P9lF56Sl7RoIt6+jJqOdRfixS6
- TDoLoXsNbOxQ+qV3B8pU2Tam204= )
- 14400 NSEC ns.example.org. A RRSIG NSEC
- 14400 RRSIG NSEC 5 4 14400 (
- 20170702091734 20170602091734 54282 example.org.
- Tmu27q3+xfONSZZtZLhejBUVtEw+83ZU1AFb
- Rsxctjry/x5r2JSxw/sgSAExxX/7tx/okZ8J
- oJqtChpsr91Kiw3eEBgINi2lCYIpMJlW4cWz
- 8bYlHfR81VsKYgy/cRgrq1RRvBoJnw+nwSty
- mKPIvUtt67LAvLxJheSCEMZLCKI= )
-ns.example.org. 1800 IN A 127.0.0.1
- 1800 RRSIG A 5 3 1800 (
- 20170702091734 20170602091734 54282 example.org.
- mhi1SGaaAt+ndQEg5uKWKCH0HMzaqh/9dUK3
- p2wWMBrLbTZrcWyz10zRnvehicXDCasbBrer
- ZpDQnz5AgxYYBURvdPfUzx1XbNuRJRE4l5PN
- CEUTlTWcqCXnlSoPKEJE5HRf7v0xg2BrBUfM
- 4mZnW2bFLwjrRQ5mm/mAmHmTROk= )
- 14400 NSEC example.org. A RRSIG NSEC
- 14400 RRSIG NSEC 5 3 14400 (
- 20170702091734 20170602091734 54282 example.org.
- loHcdjX+NIWLAkUDfPSy2371wrfUvrBQTfMO
- 17eO2Y9E/6PE935NF5bjQtZBRRghyxzrFJhm
- vY1Ad5ZTb+NLHvdSWbJQJog+eCc7QWp64WzR
- RXpMdvaE6ZDwalWldLjC3h8QDywDoFdndoRY
- eHOsmTvvtWWqtO6Fa5A8gmHT5HA= )
-`
diff --git a/middleware/file/dnssec_test.go b/middleware/file/dnssec_test.go
deleted file mode 100644
index 136a9dfee..000000000
--- a/middleware/file/dnssec_test.go
+++ /dev/null
@@ -1,358 +0,0 @@
-package file
-
-import (
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-var dnssecTestCases = []test.Case{
- {
- Qname: "miek.nl.", Qtype: dns.TypeSOA, Do: true,
- Answer: []dns.RR{
- test.RRSIG("miek.nl. 1800 IN RRSIG SOA 8 2 1800 20160426031301 20160327031301 12051 miek.nl. FIrzy07acBbtyQczy1dc="),
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- Ns: auth,
- Extra: []dns.RR{test.OPT(4096, true)},
- },
- {
- Qname: "miek.nl.", Qtype: dns.TypeAAAA, Do: true,
- Answer: []dns.RR{
- test.AAAA("miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- test.RRSIG("miek.nl. 1800 IN RRSIG AAAA 8 2 1800 20160426031301 20160327031301 12051 miek.nl. SsRT="),
- },
- Ns: auth,
- Extra: []dns.RR{test.OPT(4096, true)},
- },
- {
- Qname: "miek.nl.", Qtype: dns.TypeNS, Do: true,
- Answer: []dns.RR{
- test.NS("miek.nl. 1800 IN NS ext.ns.whyscream.net."),
- test.NS("miek.nl. 1800 IN NS linode.atoom.net."),
- test.NS("miek.nl. 1800 IN NS ns-ext.nlnetlabs.nl."),
- test.NS("miek.nl. 1800 IN NS omval.tednet.nl."),
- test.RRSIG("miek.nl. 1800 IN RRSIG NS 8 2 1800 20160426031301 20160327031301 12051 miek.nl. ZLtsQhwaz+lHfNpztFoR1Vxs="),
- },
- Extra: []dns.RR{test.OPT(4096, true)},
- },
- {
- Qname: "miek.nl.", Qtype: dns.TypeMX, Do: true,
- Answer: []dns.RR{
- test.MX("miek.nl. 1800 IN MX 1 aspmx.l.google.com."),
- test.MX("miek.nl. 1800 IN MX 10 aspmx2.googlemail.com."),
- test.MX("miek.nl. 1800 IN MX 10 aspmx3.googlemail.com."),
- test.MX("miek.nl. 1800 IN MX 5 alt1.aspmx.l.google.com."),
- test.MX("miek.nl. 1800 IN MX 5 alt2.aspmx.l.google.com."),
- test.RRSIG("miek.nl. 1800 IN RRSIG MX 8 2 1800 20160426031301 20160327031301 12051 miek.nl. kLqG+iOr="),
- },
- Ns: auth,
- Extra: []dns.RR{test.OPT(4096, true)},
- },
- {
- Qname: "www.miek.nl.", Qtype: dns.TypeA, Do: true,
- Answer: []dns.RR{
- test.A("a.miek.nl. 1800 IN A 139.162.196.78"),
- test.RRSIG("a.miek.nl. 1800 IN RRSIG A 8 3 1800 20160426031301 20160327031301 12051 miek.nl. lxLotCjWZ3kihTxk="),
- test.CNAME("www.miek.nl. 1800 IN CNAME a.miek.nl."),
- test.RRSIG("www.miek.nl. 1800 RRSIG CNAME 8 3 1800 20160426031301 20160327031301 12051 miek.nl. NVZmMJaypS+wDL2Lar4Zw1zF"),
- },
- Ns: auth,
- Extra: []dns.RR{
- test.OPT(4096, true),
- },
- },
- {
- // NoData
- Qname: "a.miek.nl.", Qtype: dns.TypeSRV, Do: true,
- Ns: []dns.RR{
- test.NSEC("a.miek.nl. 14400 IN NSEC archive.miek.nl. A AAAA RRSIG NSEC"),
- test.RRSIG("a.miek.nl. 14400 IN RRSIG NSEC 8 3 14400 20160426031301 20160327031301 12051 miek.nl. GqnF6cutipmSHEao="),
- test.RRSIG("miek.nl. 1800 IN RRSIG SOA 8 2 1800 20160426031301 20160327031301 12051 miek.nl. FIrzy07acBbtyQczy1dc="),
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- Extra: []dns.RR{test.OPT(4096, true)},
- },
- {
- Qname: "b.miek.nl.", Qtype: dns.TypeA, Do: true,
- Rcode: dns.RcodeNameError,
- Ns: []dns.RR{
- test.NSEC("archive.miek.nl. 14400 IN NSEC go.dns.miek.nl. CNAME RRSIG NSEC"),
- test.RRSIG("archive.miek.nl. 14400 IN RRSIG NSEC 8 3 14400 20160426031301 20160327031301 12051 miek.nl. jEpx8lcp4do5fWXg="),
- test.NSEC("miek.nl. 14400 IN NSEC a.miek.nl. A NS SOA MX AAAA RRSIG NSEC DNSKEY"),
- test.RRSIG("miek.nl. 14400 IN RRSIG NSEC 8 2 14400 20160426031301 20160327031301 12051 miek.nl. mFfc3r/9PSC1H6oSpdC"),
- test.RRSIG("miek.nl. 1800 IN RRSIG SOA 8 2 1800 20160426031301 20160327031301 12051 miek.nl. FIrzy07acBbtyQczy1dc="),
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- Extra: []dns.RR{test.OPT(4096, true)},
- },
- {
- Qname: "b.blaat.miek.nl.", Qtype: dns.TypeA, Do: true,
- Rcode: dns.RcodeNameError,
- Ns: []dns.RR{
- test.NSEC("archive.miek.nl. 14400 IN NSEC go.dns.miek.nl. CNAME RRSIG NSEC"),
- test.RRSIG("archive.miek.nl. 14400 IN RRSIG NSEC 8 3 14400 20160426031301 20160327031301 12051 miek.nl. jEpx8lcp4do5fWXg="),
- test.NSEC("miek.nl. 14400 IN NSEC a.miek.nl. A NS SOA MX AAAA RRSIG NSEC DNSKEY"),
- test.RRSIG("miek.nl. 14400 IN RRSIG NSEC 8 2 14400 20160426031301 20160327031301 12051 miek.nl. mFfc3r/9PSC1H6oSpdC"),
- test.RRSIG("miek.nl. 1800 IN RRSIG SOA 8 2 1800 20160426031301 20160327031301 12051 miek.nl. FIrzy07acBbtyQczy1dc="),
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- Extra: []dns.RR{test.OPT(4096, true)},
- },
- {
- Qname: "b.a.miek.nl.", Qtype: dns.TypeA, Do: true,
- Rcode: dns.RcodeNameError,
- Ns: []dns.RR{
- // dedupped NSEC, because 1 nsec tells all
- test.NSEC("a.miek.nl. 14400 IN NSEC archive.miek.nl. A AAAA RRSIG NSEC"),
- test.RRSIG("a.miek.nl. 14400 IN RRSIG NSEC 8 3 14400 20160426031301 20160327031301 12051 miek.nl. GqnF6cut/RRGPQ1QGQE1ipmSHEao="),
- test.RRSIG("miek.nl. 1800 IN RRSIG SOA 8 2 1800 20160426031301 20160327031301 12051 miek.nl. FIrzy07acBbtyQczy1dc="),
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- Extra: []dns.RR{test.OPT(4096, true)},
- },
-}
-
-var auth = []dns.RR{
- test.NS("miek.nl. 1800 IN NS ext.ns.whyscream.net."),
- test.NS("miek.nl. 1800 IN NS linode.atoom.net."),
- test.NS("miek.nl. 1800 IN NS ns-ext.nlnetlabs.nl."),
- test.NS("miek.nl. 1800 IN NS omval.tednet.nl."),
- test.RRSIG("miek.nl. 1800 IN RRSIG NS 8 2 1800 20160426031301 20160327031301 12051 miek.nl. ZLtsQhwazbqSpztFoR1Vxs="),
-}
-
-func TestLookupDNSSEC(t *testing.T) {
- zone, err := Parse(strings.NewReader(dbMiekNLSigned), testzone, "stdin", 0)
- if err != nil {
- t.Fatalf("Expected no error when reading zone, got %q", err)
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{testzone: zone}, Names: []string{testzone}}}
- ctx := context.TODO()
-
- for _, tc := range dnssecTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-func BenchmarkFileLookupDNSSEC(b *testing.B) {
- zone, err := Parse(strings.NewReader(dbMiekNLSigned), testzone, "stdin", 0)
- if err != nil {
- return
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{testzone: zone}, Names: []string{testzone}}}
- ctx := context.TODO()
- rec := dnsrecorder.New(&test.ResponseWriter{})
-
- tc := test.Case{
- Qname: "b.miek.nl.", Qtype: dns.TypeA, Do: true,
- Rcode: dns.RcodeNameError,
- Ns: []dns.RR{
- test.NSEC("archive.miek.nl. 14400 IN NSEC go.dns.miek.nl. CNAME RRSIG NSEC"),
- test.RRSIG("archive.miek.nl. 14400 IN RRSIG NSEC 8 3 14400 20160426031301 20160327031301 12051 miek.nl. jEpx8lcp4do5fWXg="),
- test.NSEC("miek.nl. 14400 IN NSEC a.miek.nl. A NS SOA MX AAAA RRSIG NSEC DNSKEY"),
- test.RRSIG("miek.nl. 14400 IN RRSIG NSEC 8 2 14400 20160426031301 20160327031301 12051 miek.nl. mFfc3r/9PSC1H6oSpdC"),
- test.RRSIG("miek.nl. 1800 IN RRSIG SOA 8 2 1800 20160426031301 20160327031301 12051 miek.nl. FIrzy07acBbtyQczy1dc="),
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- }
-
- m := tc.Msg()
-
- b.ResetTimer()
-
- for i := 0; i < b.N; i++ {
- fm.ServeDNS(ctx, rec, m)
- }
-}
-
-const dbMiekNLSigned = `
-; File written on Sun Mar 27 04:13:01 2016
-; dnssec_signzone version 9.10.3-P4-Ubuntu
-miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. (
- 1459051981 ; serial
- 14400 ; refresh (4 hours)
- 3600 ; retry (1 hour)
- 604800 ; expire (1 week)
- 14400 ; minimum (4 hours)
- )
- 1800 RRSIG SOA 8 2 1800 (
- 20160426031301 20160327031301 12051 miek.nl.
- FIrzy07acBzrf6kNW13Ypmq/ahojoMqOj0qJ
- ixTevTvwOEcVuw9GlJoYIHTYg+hm1sZHtx9K
- RiVmYsm8SHKsJA1WzixtT4K7vQvM+T+qbeOJ
- xA6YTivKUcGRWRXQlOTUAlHS/KqBEfmxKgRS
- 68G4oOEClFDSJKh7RbtyQczy1dc= )
- 1800 NS ext.ns.whyscream.net.
- 1800 NS omval.tednet.nl.
- 1800 NS linode.atoom.net.
- 1800 NS ns-ext.nlnetlabs.nl.
- 1800 RRSIG NS 8 2 1800 (
- 20160426031301 20160327031301 12051 miek.nl.
- ZLtsQhwaz+CwrgzgFiEAqbqS/JH65MYjziA3
- 6EXwlGDy41lcfGm71PpxA7cDzFhWNkJNk4QF
- q48wtpP4IGPPpHbnJHKDUXj6se7S+ylAGbS+
- VgVJ4YaVcE6xA9ZVhVpz8CSSjeH34vmqq9xj
- zmFjofuDvraZflHfNpztFoR1Vxs= )
- 1800 A 139.162.196.78
- 1800 RRSIG A 8 2 1800 (
- 20160426031301 20160327031301 12051 miek.nl.
- hl+6Q075tsCkxIqbop8zZ6U8rlFvooz7Izzx
- MgCZYVLcg75El28EXKIhBfRb1dPaKbd+v+AD
- wrJMHL131pY5sU2Ly05K+7CqmmyaXgDaVsKS
- rSw/TbhGDIItBemeseeuXGAKAbY2+gE7kNN9
- mZoQ9hRB3SrxE2jhctv66DzYYQQ= )
- 1800 MX 1 aspmx.l.google.com.
- 1800 MX 5 alt1.aspmx.l.google.com.
- 1800 MX 5 alt2.aspmx.l.google.com.
- 1800 MX 10 aspmx2.googlemail.com.
- 1800 MX 10 aspmx3.googlemail.com.
- 1800 RRSIG MX 8 2 1800 (
- 20160426031301 20160327031301 12051 miek.nl.
- kLqG+iOrKSzms1H9Et9me8Zts1rbyeCFSVQD
- G9is/u6ec3Lqg2vwJddf/yRsjVpVgadWSAkc
- GSDuD2dK8oBeP24axWc3Z1OY2gdMI7w+PKWT
- Z+pjHVjbjM47Ii/a6jk5SYeOwpGMsdEwhtTP
- vk2O2WGljifqV3uE7GshF5WNR10= )
- 1800 AAAA 2a01:7e00::f03c:91ff:fef1:6735
- 1800 RRSIG AAAA 8 2 1800 (
- 20160426031301 20160327031301 12051 miek.nl.
- SsRTHytW4YTAuHovHQgfIMhNwMtMp4gaAU/Z
- lgTO+IkBb9y9F8uHrf25gG6RqA1bnGV/gezV
- NU5negXm50bf1BNcyn3aCwEbA0rCGYIL+nLJ
- szlBVbBu6me/Ym9bbJlfgfHRDfsVy2ZkNL+B
- jfNQtGCSDoJwshjcqJlfIVSardo= )
- 14400 NSEC a.miek.nl. A NS SOA MX AAAA RRSIG NSEC DNSKEY
- 14400 RRSIG NSEC 8 2 14400 (
- 20160426031301 20160327031301 12051 miek.nl.
- mFfc3r/9PSC1H6oSpdC+FDy/Iu02W2Tf0x+b
- n6Lpe1gCC1uvcSUrrmBNlyAWRr5Zm+ZXssEb
- cKddRGiu/5sf0bUWrs4tqokL/HUl10X/sBxb
- HfwNAeD7R7+CkpMv67li5AhsDgmQzpX2r3P6
- /6oZyLvODGobysbmzeWM6ckE8IE= )
- 1800 DNSKEY 256 3 8 (
- AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6
- E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5EC
- IoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb
- 2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXH
- Py7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz
- ) ; ZSK; alg = RSASHA256; key id = 12051
- 1800 DNSKEY 257 3 8 (
- AwEAAcWdjBl4W4wh/hPxMDcBytmNCvEngIgB
- 9Ut3C2+QI0oVz78/WK9KPoQF7B74JQ/mjO4f
- vIncBmPp6mFNxs9/WQX0IXf7oKviEVOXLjct
- R4D1KQLX0wprvtUIsQFIGdXaO6suTT5eDbSd
- 6tTwu5xIkGkDmQhhH8OQydoEuCwV245ZwF/8
- AIsqBYDNQtQ6zhd6jDC+uZJXg/9LuPOxFHbi
- MTjp6j3CCW0kHbfM/YHZErWWtjPj3U3Z7knQ
- SIm5PO5FRKBEYDdr5UxWJ/1/20SrzI3iztvP
- wHDsA2rdHm/4YRzq7CvG4N0t9ac/T0a0Sxba
- /BUX2UVPWaIVBdTRBtgHi0s=
- ) ; KSK; alg = RSASHA256; key id = 33694
- 1800 RRSIG DNSKEY 8 2 1800 (
- 20160426031301 20160327031301 12051 miek.nl.
- o/D6o8+/bNGQyyRvwZ2hM0BJ+3HirvNjZoko
- yGhGe9sPSrYU39WF3JVIQvNJFK6W3/iwlKir
- TPOeYlN6QilnztFq1vpCxwj2kxJaIJhZecig
- LsKxY/fOHwZlIbBLZZadQG6JoGRLHnImSzpf
- xtyVaXQtfnJFC07HHt9np3kICfE= )
- 1800 RRSIG DNSKEY 8 2 1800 (
- 20160426031301 20160327031301 33694 miek.nl.
- Ak/mbbQVQV+nUgw5Sw/c+TSoYqIwbLARzuNE
- QJvJNoRR4tKVOY6qSxQv+j5S7vzyORZ+yeDp
- NlEa1T9kxZVBMABoOtLX5kRqZncgijuH8fxb
- L57Sv2IzINI9+DOcy9Q9p9ygtwYzQKrYoNi1
- 0hwHi6emGkVG2gGghruMinwOJASGgQy487Yd
- eIpcEKJRw73nxd2le/4/Vafy+mBpKWOczfYi
- 5m9MSSxcK56NFYjPG7TvdIw0m70F/smY9KBP
- pGWEdzRQDlqfZ4fpDaTAFGyRX0mPFzMbs1DD
- 3hQ4LHUSi/NgQakdH9eF42EVEDeL4cI69K98
- 6NNk6X9TRslO694HKw== )
-a.miek.nl. 1800 IN A 139.162.196.78
- 1800 RRSIG A 8 3 1800 (
- 20160426031301 20160327031301 12051 miek.nl.
- lxLotCjWZ3kikNNcePu6HOCqMHDINKFRJRD8
- laz2KQ9DKtgXPdnRw5RJvVITSj8GUVzw1ec1
- CYVEKu/eMw/rc953Zns528QBypGPeMNLe2vu
- C6a6UhZnGHA48dSd9EX33eSJs0MP9xsC9csv
- LGdzYmv++eslkKxkhSOk2j/hTxk= )
- 1800 AAAA 2a01:7e00::f03c:91ff:fef1:6735
- 1800 RRSIG AAAA 8 3 1800 (
- 20160426031301 20160327031301 12051 miek.nl.
- ji3QMlaUzlK85ppB5Pc+y2WnfqOi6qrm6dm1
- bXgsEov/5UV1Lmcv8+Y5NBbTbBlXGlWcpqNp
- uWpf9z3lbguDWznpnasN2MM8t7yxo/Cr7WRf
- QCzui7ewpWiA5hq7j0kVbM4nnDc6cO+U93hO
- mMhVbeVI70HM2m0HaHkziEyzVZk= )
- 14400 NSEC archive.miek.nl. A AAAA RRSIG NSEC
- 14400 RRSIG NSEC 8 3 14400 (
- 20160426031301 20160327031301 12051 miek.nl.
- GqnF6cut/KCxbnJj27MCjjVGkjObV0hLhHOP
- E1/GXAUTEKG6BWxJq8hidS3p/yrOmP5PEL9T
- 4FjBp0/REdVmGpuLaiHyMselES82p/uMMdY5
- QqRM6LHhZdO1zsRbyzOZbm5MsW6GR7K2kHlX
- 9TdBIULiRRGPQ1QGQE1ipmSHEao= )
-archive.miek.nl. 1800 IN CNAME a.miek.nl.
- 1800 RRSIG CNAME 8 3 1800 (
- 20160426031301 20160327031301 12051 miek.nl.
- s4zVJiDrVuUiUFr8CNQLuXYYfpqpl8rovL50
- BYsub/xK756NENiOTAOjYH6KYg7RSzsygJjV
- YQwXolZly2/KXAr48SCtxzkGFxLexxiKcFaj
- vm7ZDl7Btoa5l68qmBcxOX5E/W0IKITi4PNK
- mhBs7dlaf0IbPGNgMxae72RosxM= )
- 14400 NSEC go.dns.miek.nl. CNAME RRSIG NSEC
- 14400 RRSIG NSEC 8 3 14400 (
- 20160426031301 20160327031301 12051 miek.nl.
- jEp7LsoK++/PRFh2HieLzasA1jXBpp90NyDf
- RfpfOxdM69yRKfvXMc2bazIiMuDhxht79dGI
- Gj02cn1cvX60SlaHkeFtqTdJcHdK9rbI65EK
- YHFZFzGh9XVnuMJKpUsm/xS1dnUSAnXN8q+0
- xBlUDlQpsAFv/cx8lcp4do5fWXg= )
-go.dns.miek.nl. 1800 IN TXT "Hello!"
- 1800 RRSIG TXT 8 4 1800 (
- 20160426031301 20160327031301 12051 miek.nl.
- O0uo1NsXTq2TTfgOmGbHQQEchrcpllaDAMMX
- dTDizw3t+vZ5SR32qJ8W7y6VXLgUqJgcdRxS
- Fou1pp+t5juRZSQ0LKgxMpZAgHorkzPvRf1b
- E9eBKrDSuLGagsQRwHeldFGFgsXtCbf07vVH
- zoKR8ynuG4/cAoY0JzMhCts+56U= )
- 14400 NSEC www.miek.nl. TXT RRSIG NSEC
- 14400 RRSIG NSEC 8 4 14400 (
- 20160426031301 20160327031301 12051 miek.nl.
- BW6qo7kYe3Z+Y0ebaVTWTy1c3bpdf8WUEoXq
- WDQxLDEj2fFiuEBDaSN5lTWRg3wj8kZmr6Uk
- LvX0P29lbATFarIgkyiAdbOEdaf88nMfqBW8
- z2T5xrPQcN0F13uehmv395yAJs4tebRxErMl
- KdkVF0dskaDvw8Wo3YgjHUf6TXM= )
-www.miek.nl. 1800 IN CNAME a.miek.nl.
- 1800 RRSIG CNAME 8 3 1800 (
- 20160426031301 20160327031301 12051 miek.nl.
- MiQQh2lScoNiNVZmMJaypS+wDL2Lar4Zw1zF
- Uo4tL16BfQOt7yl8gXdAH2JMFqoKAoIdM2K6
- XwFOwKTOGSW0oNCOcaE7ts+1Z1U0H3O2tHfq
- FAzfg1s9pQ5zxk8J/bJgkVIkw2/cyB0y1/PK
- EmIqvChBSb4NchTuMCSqo63LJM8= )
- 14400 NSEC miek.nl. CNAME RRSIG NSEC
- 14400 RRSIG NSEC 8 3 14400 (
- 20160426031301 20160327031301 12051 miek.nl.
- OPPZ8iaUPrVKEP4cqeCiiv1WLRAY30GRIhc/
- me0gBwFkbmTEnvB+rUp831OJZDZBNKv4QdZj
- Uyc26wKUOQeUyMJqv4IRDgxH7nq9GB5JRjYZ
- IVxtGD1aqWLXz+8aMaf9ARJjtYUd3K4lt8Wz
- LbJSo5Wdq7GOWqhgkY5n3XD0/FA= )`
diff --git a/middleware/file/dnssex_test.go b/middleware/file/dnssex_test.go
deleted file mode 100644
index d9a0a4568..000000000
--- a/middleware/file/dnssex_test.go
+++ /dev/null
@@ -1,145 +0,0 @@
-package file
-
-const dbDnssexNLSigned = `
-; File written on Tue Mar 29 21:02:24 2016
-; dnssec_signzone version 9.10.3-P4-Ubuntu
-dnssex.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. (
- 1459281744 ; serial
- 14400 ; refresh (4 hours)
- 3600 ; retry (1 hour)
- 604800 ; expire (1 week)
- 14400 ; minimum (4 hours)
- )
- 1800 RRSIG SOA 8 2 1800 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- CA/Y3m9hCOiKC/8ieSOv8SeP964BUdG/8MC3
- WtKljUosK9Z9bBGrVizDjjqgq++lyH8BZJcT
- aabAsERs4xj5PRtcxicwQXZACX5VYjXHQeZm
- CyytFU5wq2gcXSmvUH86zZzftx3RGPvn1aOo
- TlcvoC3iF8fYUCpROlUS0YR8Cdw= )
- 1800 NS omval.tednet.nl.
- 1800 NS linode.atoom.net.
- 1800 NS ns-ext.nlnetlabs.nl.
- 1800 RRSIG NS 8 2 1800 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- dLIeEvP86jj5nd3orv9bH7hTvkblF4Na0sbl
- k6fJA6ha+FPN1d6Pig3NNEEVQ/+wlOp/JTs2
- v07L7roEEUCbBprI8gMSld2gFDwNLW3DAB4M
- WD/oayYdAnumekcLzhgvWixTABjWAGRTGQsP
- sVDFXsGMf9TGGC9FEomgkCVeNC0= )
- 1800 A 139.162.196.78
- 1800 RRSIG A 8 2 1800 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- LKJKLzPiSEDWOLAag2YpfD5EJCuDcEAJu+FZ
- Xy+4VyOv9YvRHCTL4vbrevOo5+XymY2RxU1q
- j+6leR/Fe7nlreSj2wzAAk2bIYn4m6r7hqeO
- aKZsUFfpX8cNcFtGEywfHndCPELbRxFeEziP
- utqHFLPNMX5nYCpS28w4oJ5sAnM= )
- 1800 TXT "Doing It Safe Is Better"
- 1800 RRSIG TXT 8 2 1800 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- f6S+DUfJK1UYdOb3AHgUXzFTTtu+yLp/Fv7S
- Hv0CAGhXAVw+nBbK719igFvBtObS33WKwzxD
- 1pQNMaJcS6zeevtD+4PKB1KDC4fyJffeEZT6
- E30jGR8Y29/xA+Fa4lqDNnj9zP3b8TiABCle
- ascY5abkgWCALLocFAzFJQ/27YQ= )
- 1800 AAAA 2a01:7e00::f03c:91ff:fef1:6735
- 1800 RRSIG AAAA 8 2 1800 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- PWcPSawEUBAfCuv0liEOQ8RYe7tfNW4rubIJ
- LE+dbrub1DUer3cWrDoCYFtOufvcbkYJQ2CQ
- AGjJmAQ5J2aqYDOPMrKa615V0KT3ifbZJcGC
- gkIic4U/EXjaQpRoLdDzR9MyVXOmbA6sKYzj
- ju1cNkLqM8D7Uunjl4pIr6rdSFo= )
- 14400 NSEC *.dnssex.nl. A NS SOA TXT AAAA RRSIG NSEC DNSKEY
- 14400 RRSIG NSEC 8 2 14400 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- oIvM6JZIlNc1aNKGTxv58ApSnDr1nDPPgnD9
- 9oJZRIn7eb5WnpeDz2H3z5+x6Bhlp5hJJaUp
- KJ3Ss6Jg/IDnrmIvKmgq6L6gHj1Y1IiHmmU8
- VeZTRzdTsDx/27OsN23roIvsytjveNSEMfIm
- iLZ23x5kg1kBdJ9p3xjYHm5lR+8= )
- 1800 DNSKEY 256 3 8 (
- AwEAAazSO6uvLPEVknDA8yxjFe8nnAMU7txp
- wb19k55hQ81WV3G4bpBM1NdN6sbYHrkXaTNx
- 2bQWAkvX6pz0XFx3z/MPhW+vkakIWFYpyQ7R
- AT5LIJfToVfiCDiyhhF0zVobKBInO9eoGjd9
- BAW3TUt+LmNAO/Ak5D5BX7R3CuA7v9k7
- ) ; ZSK; alg = RSASHA256; key id = 14460
- 1800 DNSKEY 257 3 8 (
- AwEAAbyeaV9zg0IqdtgYoqK5jJ239anzwG2i
- gvH1DxSazLyaoNvEkCIvPgMLW/JWfy7Z1mQp
- SMy9DtzL5pzRyQgw7kIeXLbi6jufUFd9pxN+
- xnzKLf9mY5AcnGToTrbSL+jnMT67wG+c34+Q
- PeVfucHNUePBxsbz2+4xbXiViSQyCQGv
- ) ; KSK; alg = RSASHA256; key id = 18772
- 1800 RRSIG DNSKEY 8 2 1800 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- cFSFtJE+DBGNxb52AweFaVHBe5Ue5MDpqNdC
- TIneUnEhP2m+vK4zJ/TraK0WdQFpsX63pod8
- PZ9y03vHUfewivyonCCBD3DcNdoU9subhN22
- tez9Ct8Z5/9E4RAz7orXal4M1VUEhRcXSEH8
- SJW20mfVsqJAiKqqNeGB/pAj23I= )
- 1800 RRSIG DNSKEY 8 2 1800 (
- 20160428190224 20160329190224 18772 dnssex.nl.
- oiiwo/7NYacePqohEp50261elhm6Dieh4j2S
- VZGAHU5gqLIQeW9CxKJKtSCkBVgUo4cvO4Rn
- 2tzArAuclDvBrMXRIoct8u7f96moeFE+x5FI
- DYqICiV6k449ljj9o4t/5G7q2CRsEfxZKpTI
- A/L0+uDk0RwVVzL45+TnilcsmZs= )
-*.dnssex.nl. 1800 IN TXT "Doing It Safe Is Better"
- 1800 RRSIG TXT 8 2 1800 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- FUZSTyvZfeuuOpCmNzVKOfITRHJ6/ygjmnnb
- XGBxVUyQjoLuYXwD5XqZWGw4iKH6QeSDfGCx
- 4MPqA4qQmW7Wwth7mat9yMfA4+p2sO84bysl
- 7/BG9+W2G+q1uQiM9bX9V42P2X/XuW5Y/t9Y
- 8u1sljQ7D8WwS6naH/vbaJxnDBw= )
- 14400 NSEC a.dnssex.nl. TXT RRSIG NSEC
- 14400 RRSIG NSEC 8 2 14400 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- os6INm6q2eXknD5z8TpfbK00uxVbQefMvHcR
- /RNX/kh0xXvzAaaDOV+Ge/Ko+2dXnKP+J1LY
- G9ffXNpdbaQy5ygzH5F041GJst4566GdG/jt
- 7Z7vLHYxEBTpZfxo+PLsXQXH3VTemZyuWyDf
- qJzafXJVH1F0nDrcXmMlR6jlBHA= )
-www.dnssex.nl. 1800 IN CNAME a.dnssex.nl.
- 1800 RRSIG CNAME 8 3 1800 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- Omv42q/uVvdNsWQoSrQ6m6w6U7r7Abga7uF4
- 25b3gZlse0C+WyMyGFMGUbapQm7azvBpreeo
- uKJHjzd+ufoG+Oul6vU9vyoj+ejgHzGLGbJQ
- HftfP+UqP5SWvAaipP/LULTWKPuiBcLDLiBI
- PGTfsq0DB6R+qCDTV0fNnkgxEBQ= )
- 14400 NSEC dnssex.nl. CNAME RRSIG NSEC
- 14400 RRSIG NSEC 8 3 14400 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- TBN3ddfZW+kC84/g3QlNNJMeLZoyCalPQylt
- KXXLPGuxfGpl3RYRY8KaHbP+5a8MnHjqjuMB
- Lofb7yKMFxpSzMh8E36vnOqry1mvkSakNj9y
- 9jM8PwDjcpYUwn/ql76MsmNgEV5CLeQ7lyH4
- AOrL79yOSQVI3JHJIjKSiz88iSw= )
-a.dnssex.nl. 1800 IN A 139.162.196.78
- 1800 RRSIG A 8 3 1800 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- OXHpFj9nSpKi5yA/ULH7MOpGAWfyJ2yC/2xa
- Pw0fqSY4QvcRt+V3adcFA4H9+P1b32GpxEjB
- lXmCJID+H4lYkhUR4r4IOZBVtKG2SJEBZXip
- pH00UkOIBiXxbGzfX8VL04v2G/YxUgLW57kA
- aknaeTOkJsO20Y+8wmR9EtzaRFI= )
- 1800 AAAA 2a01:7e00::f03c:91ff:fef1:6735
- 1800 RRSIG AAAA 8 3 1800 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- jrepc/VnRzJypnrG0WDEqaAr3HMjWrPxJNX0
- 86gbFjZG07QxBmrA1rj0jM9YEWTjjyWb2tT7
- lQhzKDYX/0XdOVUeeOM4FoSks80V+pWR8fvj
- AZ5HmX69g36tLosMDKNR4lXcrpv89QovG4Hr
- /r58fxEKEFJqrLDjMo6aOrg+uKA= )
- 14400 NSEC www.dnssex.nl. A AAAA RRSIG NSEC
- 14400 RRSIG NSEC 8 3 14400 (
- 20160428190224 20160329190224 14460 dnssex.nl.
- S+UM62wXRNNFN3QDWK5YFWUbHBXC4aqaqinZ
- A2ZDeC+IQgyw7vazPz7cLI5T0YXXks0HTMlr
- soEjKnnRZsqSO9EuUavPNE1hh11Jjm0fB+5+
- +Uro0EmA5Dhgc0Z2VpbXVQEhNDf/pI1gem15
- RffN2tBYNykZn4Has2ySgRaaRYQ= )`
diff --git a/middleware/file/ds_test.go b/middleware/file/ds_test.go
deleted file mode 100644
index f5c9bb332..000000000
--- a/middleware/file/ds_test.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package file
-
-import (
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-var dsTestCases = []test.Case{
- {
- Qname: "a.delegated.miek.nl.", Qtype: dns.TypeDS,
- Ns: []dns.RR{
- test.NS("delegated.miek.nl. 1800 IN NS a.delegated.miek.nl."),
- test.NS("delegated.miek.nl. 1800 IN NS ns-ext.nlnetlabs.nl."),
- },
- Extra: []dns.RR{
- test.A("a.delegated.miek.nl. 1800 IN A 139.162.196.78"),
- test.AAAA("a.delegated.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- },
- {
- Qname: "_udp.delegated.miek.nl.", Qtype: dns.TypeDS,
- Ns: []dns.RR{
- test.NS("delegated.miek.nl. 1800 IN NS a.delegated.miek.nl."),
- test.NS("delegated.miek.nl. 1800 IN NS ns-ext.nlnetlabs.nl."),
- },
- Extra: []dns.RR{
- test.A("a.delegated.miek.nl. 1800 IN A 139.162.196.78"),
- test.AAAA("a.delegated.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- },
- {
- // This works *here* because we skip the server routing for DS in core/dnsserver/server.go
- Qname: "_udp.miek.nl.", Qtype: dns.TypeDS,
- Rcode: dns.RcodeNameError,
- Ns: []dns.RR{
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- },
- {
- Qname: "miek.nl.", Qtype: dns.TypeDS,
- Ns: []dns.RR{
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- },
-}
-
-func TestLookupDS(t *testing.T) {
- zone, err := Parse(strings.NewReader(dbMiekNLDelegation), testzone, "stdin", 0)
- if err != nil {
- t.Fatalf("Expected no error when reading zone, got %q", err)
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{testzone: zone}, Names: []string{testzone}}}
- ctx := context.TODO()
-
- for _, tc := range dsTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
diff --git a/middleware/file/ent_test.go b/middleware/file/ent_test.go
deleted file mode 100644
index 433aa182d..000000000
--- a/middleware/file/ent_test.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package file
-
-import (
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-var entTestCases = []test.Case{
- {
- Qname: "b.c.miek.nl.", Qtype: dns.TypeA,
- Ns: []dns.RR{
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- },
- {
- Qname: "b.c.miek.nl.", Qtype: dns.TypeA, Do: true,
- Ns: []dns.RR{
- test.NSEC("a.miek.nl. 14400 IN NSEC a.b.c.miek.nl. A RRSIG NSEC"),
- test.RRSIG("a.miek.nl. 14400 IN RRSIG NSEC 8 3 14400 20160502144311 20160402144311 12051 miek.nl. d5XZEy6SUpq98ZKUlzqhAfkLI9pQPc="),
- test.RRSIG("miek.nl. 1800 IN RRSIG SOA 8 2 1800 20160502144311 20160402144311 12051 miek.nl. KegoBxA3Tbrhlc4cEdkRiteIkOfsq"),
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- Extra: []dns.RR{test.OPT(4096, true)},
- },
-}
-
-func TestLookupEnt(t *testing.T) {
- zone, err := Parse(strings.NewReader(dbMiekENTNL), testzone, "stdin", 0)
- if err != nil {
- t.Fatalf("expect no error when reading zone, got %q", err)
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{testzone: zone}, Names: []string{testzone}}}
- ctx := context.TODO()
-
- for _, tc := range entTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-// fdjfdjkf
-const dbMiekENTNL = `; File written on Sat Apr 2 16:43:11 2016
-; dnssec_signzone version 9.10.3-P4-Ubuntu
-miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. (
- 1282630057 ; serial
- 14400 ; refresh (4 hours)
- 3600 ; retry (1 hour)
- 604800 ; expire (1 week)
- 14400 ; minimum (4 hours)
- )
- 1800 RRSIG SOA 8 2 1800 (
- 20160502144311 20160402144311 12051 miek.nl.
- KegoBxA3Tbrhlc4cEdkRiteIkOfsqD4oCLLM
- ISJ5bChWy00LGHUlAnHVu5Ti96hUjVNmGSxa
- xtGSuAAMFCr52W8pAB8LBIlu9B6QZUPHMccr
- SuzxAX3ioawk2uTjm+k8AGPT4RoQdXemGLAp
- zJTASolTVmeMTh5J0sZTZJrtvZ0= )
- 1800 NS linode.atoom.net.
- 1800 RRSIG NS 8 2 1800 (
- 20160502144311 20160402144311 12051 miek.nl.
- m0cOHL6Rre/0jZPXe+0IUjs/8AFASRCvDbSx
- ZQsRDSlZgS6RoMP3OC77cnrKDVlfZ2Vhq3Ce
- nYPoGe0/atB92XXsilmstx4HTSU64gsV9iLN
- Xkzk36617t7zGOl/qumqfaUXeA9tihItzEim
- 6SGnufVZI4o8xeyaVCNDDuN0bvY= )
- 14400 NSEC a.miek.nl. NS SOA RRSIG NSEC DNSKEY
- 14400 RRSIG NSEC 8 2 14400 (
- 20160502144311 20160402144311 12051 miek.nl.
- BCWVgwxWrs4tBjS9QXKkftCUbiLi40NyH1yA
- nbFy1wCKQ2jDH00810+ia4b66QrjlAKgxE9z
- 9U7MKSMV86sNkyAtlCi+2OnjtWF6sxPdJO7k
- CHeg46XBjrQuiJRY8CneQX56+IEPdufLeqPR
- l+ocBQ2UkGhXmQdWp3CFDn2/eqU= )
- 1800 DNSKEY 256 3 8 (
- AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6
- E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5EC
- IoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb
- 2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXH
- Py7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz
- ) ; ZSK; alg = RSASHA256; key id = 12051
- 1800 DNSKEY 257 3 8 (
- AwEAAcWdjBl4W4wh/hPxMDcBytmNCvEngIgB
- 9Ut3C2+QI0oVz78/WK9KPoQF7B74JQ/mjO4f
- vIncBmPp6mFNxs9/WQX0IXf7oKviEVOXLjct
- R4D1KQLX0wprvtUIsQFIGdXaO6suTT5eDbSd
- 6tTwu5xIkGkDmQhhH8OQydoEuCwV245ZwF/8
- AIsqBYDNQtQ6zhd6jDC+uZJXg/9LuPOxFHbi
- MTjp6j3CCW0kHbfM/YHZErWWtjPj3U3Z7knQ
- SIm5PO5FRKBEYDdr5UxWJ/1/20SrzI3iztvP
- wHDsA2rdHm/4YRzq7CvG4N0t9ac/T0a0Sxba
- /BUX2UVPWaIVBdTRBtgHi0s=
- ) ; KSK; alg = RSASHA256; key id = 33694
- 1800 RRSIG DNSKEY 8 2 1800 (
- 20160502144311 20160402144311 12051 miek.nl.
- YNpi1jRDQKpnsQEjIjxqy+kJGaYnV16e8Iug
- 40c82y4pee7kIojFUllSKP44qiJpCArxF557
- tfjfwBd6c4hkqCScGPZXJ06LMyG4u//rhVMh
- 4hyKcxzQFKxmrFlj3oQGksCI8lxGX6RxiZuR
- qv2ol2lUWrqetpAL+Zzwt71884E= )
- 1800 RRSIG DNSKEY 8 2 1800 (
- 20160502144311 20160402144311 33694 miek.nl.
- jKpLDEeyadgM0wDgzEk6sBBdWr2/aCrkAOU/
- w6dYIafN98f21oIYQfscV1gc7CTsA0vwzzUu
- x0QgwxoNLMvSxxjOiW/2MzF8eozczImeCWbl
- ad/pVCYH6Jn5UBrZ5RCWMVcs2RP5KDXWeXKs
- jEN/0EmQg5qNd4zqtlPIQinA9I1HquJAnS56
- pFvYyGIbZmGEbhR18sXVBeTWYr+zOMHn2quX
- 0kkrx2udz+sPg7i4yRsLdhw138gPRy1qvbaC
- 8ELs1xo1mC9pTlDOhz24Q3iXpVAU1lXLYOh9
- nUP1/4UvZEYXHBUQk/XPRciojniWjAF825x3
- QoSivMHblBwRdAKJSg== )
-a.miek.nl. 1800 IN A 127.0.0.1
- 1800 RRSIG A 8 3 1800 (
- 20160502144311 20160402144311 12051 miek.nl.
- lUOYdSxScjyYz+Ebc+nb6iTNgCohqj7K+Dat
- 97KE7haV2nP3LxdYuDCJYZpeyhsXDLHd4bFI
- bInYPwJiC6DUCxPCuCWy0KYlZOWW8KCLX3Ia
- BOPQbvIwLsJhnX+/tyMD9mXortoqATO79/6p
- nNxvFeM8pFDwaih17fXMuFR/BsI= )
- 14400 NSEC a.b.c.miek.nl. A RRSIG NSEC
- 14400 RRSIG NSEC 8 3 14400 (
- 20160502144311 20160402144311 12051 miek.nl.
- d5XZEy6SUp+TPRJQED+0R65zf2Yeo/1dlEA2
- jYYvkXGSHXke4sg9nH8U3nr1rLcuqA1DsQgH
- uMIjdENvXuZ+WCSwvIbhC+JEI6AyQ6Gfaf/D
- I3mfu60C730IRByTrKM5C2rt11lwRQlbdaUY
- h23/nn/q98ZKUlzqhAfkLI9pQPc= )
-a.b.c.miek.nl. 1800 IN A 127.0.0.1
- 1800 RRSIG A 8 5 1800 (
- 20160502144311 20160402144311 12051 miek.nl.
- FwgU5+fFD4hEebco3gvKQt3PXfY+dcOJr8dl
- Ky4WLsONIdhP+4e9oprPisSLxImErY21BcrW
- xzu1IZrYDsS8XBVV44lBx5WXEKvAOrUcut/S
- OWhFZW7ncdIQCp32ZBIatiLRJEqXUjx+guHs
- noFLiHix35wJWsRKwjGLIhH1fbs= )
- 14400 NSEC miek.nl. A RRSIG NSEC
- 14400 RRSIG NSEC 8 5 14400 (
- 20160502144311 20160402144311 12051 miek.nl.
- lXgOqm9/jRRYvaG5jC1CDvTtGYxMroTzf4t4
- jeYGb60+qI0q9sHQKfAJvoQ5o8o1qfR7OuiF
- f544ipYT9eTcJRyGAOoJ37yMie7ZIoVJ91tB
- r8YdzZ9Q6x3v1cbwTaQiacwhPZhGYOw63qIs
- q5IQErIPos2sNk+y9D8BEce2DO4= )`
diff --git a/middleware/file/example_org.go b/middleware/file/example_org.go
deleted file mode 100644
index eba18e0e4..000000000
--- a/middleware/file/example_org.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package file
-
-// exampleOrgSigned is a fake signed example.org zone with two delegations,
-// one signed (with DSs) and one "normal".
-const exampleOrgSigned = `
-example.org. 1800 IN SOA a.iana-servers.net. devnull.example.org. (
- 1282630057 ; serial
- 14400 ; refresh (4 hours)
- 3600 ; retry (1 hour)
- 604800 ; expire (1 week)
- 14400 ; minimum (4 hours)
- )
- 1800 RRSIG SOA 13 2 1800 (
- 20161129153240 20161030153240 49035 example.org.
- GVnMpFmN+6PDdgCtlYDEYBsnBNDgYmEJNvos
- Bk9+PNTPNWNst+BXCpDadTeqRwrr1RHEAQ7j
- YWzNwqn81pN+IA== )
- 1800 NS a.iana-servers.net.
- 1800 NS b.iana-servers.net.
- 1800 RRSIG NS 13 2 1800 (
- 20161129153240 20161030153240 49035 example.org.
- llrHoIuwjnbo28LOt4p5zWAs98XGqrXicKVI
- Qxyaf/ORM8boJvW2XrKr3nj6Y8FKMhzd287D
- 5PBzVCL6MZyjQg== )
- 14400 NSEC a.example.org. NS SOA RRSIG NSEC DNSKEY
- 14400 RRSIG NSEC 13 2 14400 (
- 20161129153240 20161030153240 49035 example.org.
- BQROf1swrmYi3GqpP5M/h5vTB8jmJ/RFnlaX
- 7fjxvV7aMvXCsr3ekWeB2S7L6wWFihDYcKJg
- 9BxVPqxzBKeaqg== )
- 1800 DNSKEY 256 3 13 (
- UNTqlHbC51EbXuY0rshW19Iz8SkCuGVS+L0e
- bQj53dvtNlaKfWmtTauC797FoyVLbQwoMy/P
- G68SXgLCx8g+9g==
- ) ; ZSK; alg = ECDSAP256SHA256; key id = 49035
- 1800 RRSIG DNSKEY 13 2 1800 (
- 20161129153240 20161030153240 49035 example.org.
- LnLHyqYJaCMOt7EHB4GZxzAzWLwEGCTFiEhC
- jj1X1VuQSjJcN42Zd3yF+jihSW6huknrig0Z
- Mqv0FM6mJ/qPKg== )
-a.delegated.example.org. 1800 IN A 139.162.196.78
- 1800 TXT "obscured"
- 1800 AAAA 2a01:7e00::f03c:91ff:fef1:6735
-archive.example.org. 1800 IN CNAME a.example.org.
- 1800 RRSIG CNAME 13 3 1800 (
- 20161129153240 20161030153240 49035 example.org.
- SDFW1z/PN9knzH8BwBvmWK0qdIwMVtGrMgRw
- 7lgy4utRrdrRdCSLZy3xpkmkh1wehuGc4R0S
- 05Z3DPhB0Fg5BA== )
- 14400 NSEC delegated.example.org. CNAME RRSIG NSEC
- 14400 RRSIG NSEC 13 3 14400 (
- 20161129153240 20161030153240 49035 example.org.
- DQqLSVNl8F6v1K09wRU6/M6hbHy2VUddnOwn
- JusJjMlrAOmoOctCZ/N/BwqCXXBA+d9yFGdH
- knYumXp+BVPBAQ== )
-www.example.org. 1800 IN CNAME a.example.org.
- 1800 RRSIG CNAME 13 3 1800 (
- 20161129153240 20161030153240 49035 example.org.
- adzujOxCV0uBV4OayPGfR11iWBLiiSAnZB1R
- slmhBFaDKOKSNYijGtiVPeaF+EuZs63pzd4y
- 6Nm2Iq9cQhAwAA== )
- 14400 NSEC example.org. CNAME RRSIG NSEC
- 14400 RRSIG NSEC 13 3 14400 (
- 20161129153240 20161030153240 49035 example.org.
- jy3f96GZGBaRuQQjuqsoP1YN8ObZF37o+WkV
- PL7TruzI7iNl0AjrUDy9FplP8Mqk/HWyvlPe
- N3cU+W8NYlfDDQ== )
-a.example.org. 1800 IN A 139.162.196.78
- 1800 RRSIG A 13 3 1800 (
- 20161129153240 20161030153240 49035 example.org.
- 41jFz0Dr8tZBN4Kv25S5dD4vTmviFiLx7xSA
- qMIuLFm0qibKL07perKpxqgLqM0H1wreT4xz
- I9Y4Dgp1nsOuMA== )
- 1800 AAAA 2a01:7e00::f03c:91ff:fef1:6735
- 1800 RRSIG AAAA 13 3 1800 (
- 20161129153240 20161030153240 49035 example.org.
- brHizDxYCxCHrSKIu+J+XQbodRcb7KNRdN4q
- VOWw8wHqeBsFNRzvFF6jwPQYphGP7kZh1KAb
- VuY5ZVVhM2kHjw== )
- 14400 NSEC archive.example.org. A AAAA RRSIG NSEC
- 14400 RRSIG NSEC 13 3 14400 (
- 20161129153240 20161030153240 49035 example.org.
- zIenVlg5ScLr157EWigrTGUgrv7W/1s49Fic
- i2k+OVjZfT50zw+q5X6DPKkzfAiUhIuqs53r
- hZUzZwV/1Wew9Q== )
-delegated.example.org. 1800 IN NS a.delegated.example.org.
- 1800 IN NS ns-ext.nlnetlabs.nl.
- 1800 DS 10056 5 1 (
- EE72CABD1927759CDDA92A10DBF431504B9E
- 1F13 )
- 1800 DS 10056 5 2 (
- E4B05F87725FA86D9A64F1E53C3D0E625094
- 6599DFE639C45955B0ED416CDDFA )
- 1800 RRSIG DS 13 3 1800 (
- 20161129153240 20161030153240 49035 example.org.
- rlNNzcUmtbjLSl02ZzQGUbWX75yCUx0Mug1j
- HtKVqRq1hpPE2S3863tIWSlz+W9wz4o19OI4
- jbznKKqk+DGKog== )
- 14400 NSEC sub.example.org. NS DS RRSIG NSEC
- 14400 RRSIG NSEC 13 3 14400 (
- 20161129153240 20161030153240 49035 example.org.
- lNQ5kRTB26yvZU5bFn84LYFCjwWTmBcRCDbD
- cqWZvCSw4LFOcqbz1/wJKIRjIXIqnWIrfIHe
- fZ9QD5xZsrPgUQ== )
-sub.example.org. 1800 IN NS sub1.example.net.
- 1800 IN NS sub2.example.net.
- 14400 NSEC www.example.org. NS RRSIG NSEC
- 14400 RRSIG NSEC 13 3 14400 (
- 20161129153240 20161030153240 49035 example.org.
- VYjahdV+TTkA3RBdnUI0hwXDm6U5k/weeZZr
- ix1znORpOELbeLBMJW56cnaG+LGwOQfw9qqj
- bOuULDst84s4+g== )
-`
diff --git a/middleware/file/file.go b/middleware/file/file.go
deleted file mode 100644
index 27ab0cd1b..000000000
--- a/middleware/file/file.go
+++ /dev/null
@@ -1,138 +0,0 @@
-// Package file implements a file backend.
-package file
-
-import (
- "fmt"
- "io"
- "log"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-type (
- // File is the middleware that reads zone data from disk.
- File struct {
- Next middleware.Handler
- Zones Zones
- }
-
- // Zones maps zone names to a *Zone.
- Zones struct {
- Z map[string]*Zone // A map mapping zone (origin) to the Zone's data
- Names []string // All the keys from the map Z as a string slice.
- }
-)
-
-// ServeDNS implements the middleware.Handle interface.
-func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- state := request.Request{W: w, Req: r}
-
- qname := state.Name()
- // TODO(miek): match the qname better in the map
- zone := middleware.Zones(f.Zones.Names).Matches(qname)
- if zone == "" {
- return middleware.NextOrFailure(f.Name(), f.Next, ctx, w, r)
- }
-
- z, ok := f.Zones.Z[zone]
- if !ok || z == nil {
- return dns.RcodeServerFailure, nil
- }
-
- // This is only for when we are a secondary zones.
- if r.Opcode == dns.OpcodeNotify {
- if z.isNotify(state) {
- m := new(dns.Msg)
- m.SetReply(r)
- m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
- state.SizeAndDo(m)
- w.WriteMsg(m)
-
- log.Printf("[INFO] Notify from %s for %s: checking transfer", state.IP(), zone)
- ok, err := z.shouldTransfer()
- if ok {
- z.TransferIn()
- } else {
- log.Printf("[INFO] Notify from %s for %s: no serial increase seen", state.IP(), zone)
- }
- if err != nil {
- log.Printf("[WARNING] Notify from %s for %s: failed primary check: %s", state.IP(), zone, err)
- }
- return dns.RcodeSuccess, nil
- }
- log.Printf("[INFO] Dropping notify from %s for %s", state.IP(), zone)
- return dns.RcodeSuccess, nil
- }
-
- if z.Expired != nil && *z.Expired {
- log.Printf("[ERROR] Zone %s is expired", zone)
- return dns.RcodeServerFailure, nil
- }
-
- if state.QType() == dns.TypeAXFR || state.QType() == dns.TypeIXFR {
- xfr := Xfr{z}
- return xfr.ServeDNS(ctx, w, r)
- }
-
- answer, ns, extra, result := z.Lookup(state, qname)
-
- m := new(dns.Msg)
- m.SetReply(r)
- m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
- m.Answer, m.Ns, m.Extra = answer, ns, extra
-
- switch result {
- case Success:
- case NoData:
- case NameError:
- m.Rcode = dns.RcodeNameError
- case Delegation:
- m.Authoritative = false
- case ServerFailure:
- return dns.RcodeServerFailure, nil
- }
-
- state.SizeAndDo(m)
- m, _ = state.Scrub(m)
- w.WriteMsg(m)
- return dns.RcodeSuccess, nil
-}
-
-// Name implements the Handler interface.
-func (f File) Name() string { return "file" }
-
-// Parse parses the zone in filename and returns a new Zone or an error.
-// If serial >= 0 it will reload the zone, if the SOA hasn't changed
-// it returns an error indicating nothing was read.
-func Parse(f io.Reader, origin, fileName string, serial int64) (*Zone, error) {
- tokens := dns.ParseZone(f, dns.Fqdn(origin), fileName)
- z := NewZone(origin, fileName)
- seenSOA := false
- for x := range tokens {
- if x.Error != nil {
- return nil, x.Error
- }
-
- if !seenSOA && serial >= 0 {
- if s, ok := x.RR.(*dns.SOA); ok {
- if s.Serial == uint32(serial) { // same zone
- return nil, fmt.Errorf("no change in serial: %d", serial)
- }
- seenSOA = true
- }
- }
-
- if err := z.Insert(x.RR); err != nil {
- return nil, err
- }
- }
- if !seenSOA {
- return nil, fmt.Errorf("file %q has no SOA record", fileName)
- }
-
- return z, nil
-}
diff --git a/middleware/file/file_test.go b/middleware/file/file_test.go
deleted file mode 100644
index 02668785b..000000000
--- a/middleware/file/file_test.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package file
-
-import (
- "strings"
- "testing"
-)
-
-func BenchmarkFileParseInsert(b *testing.B) {
- for i := 0; i < b.N; i++ {
- Parse(strings.NewReader(dbMiekENTNL), testzone, "stdin", 0)
- }
-}
-
-func TestParseNoSOA(t *testing.T) {
- _, err := Parse(strings.NewReader(dbNoSOA), "example.org.", "stdin", 0)
- if err == nil {
- t.Fatalf("zone %q should have failed to load", "example.org.")
- }
- if !strings.Contains(err.Error(), "no SOA record") {
- t.Fatalf("zone %q should have failed to load with no soa error: %s", "example.org.", err)
- }
-}
-
-const dbNoSOA = `
-$TTL 1M
-$ORIGIN example.org.
-
-www IN A 192.168.0.14
-mail IN A 192.168.0.15
-imap IN CNAME mail
-`
diff --git a/middleware/file/glue_test.go b/middleware/file/glue_test.go
deleted file mode 100644
index 14716ae33..000000000
--- a/middleware/file/glue_test.go
+++ /dev/null
@@ -1,253 +0,0 @@
-package file
-
-import (
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// another personal zone (helps in testing as my secondary is NSD
-// atoom = atom in English.
-var atoomTestCases = []test.Case{
- {
- Qname: atoom, Qtype: dns.TypeNS, Do: true,
- Answer: []dns.RR{
- test.NS("atoom.net. 1800 IN NS linode.atoom.net."),
- test.NS("atoom.net. 1800 IN NS ns-ext.nlnetlabs.nl."),
- test.NS("atoom.net. 1800 IN NS omval.tednet.nl."),
- test.RRSIG("atoom.net. 1800 IN RRSIG NS 8 2 1800 20170112031301 20161213031301 53289 atoom.net. DLe+G1 jlw="),
- },
- Extra: []dns.RR{
- test.OPT(4096, true),
- test.A("linode.atoom.net. 1800 IN A 176.58.119.54"),
- test.AAAA("linode.atoom.net. 1800 IN AAAA 2a01:7e00::f03c:91ff:fe79:234c"),
- test.RRSIG("linode.atoom.net. 1800 IN RRSIG A 8 3 1800 20170112031301 20161213031301 53289 atoom.net. Z4Ka4OLDoyxj72CL vkI="),
- test.RRSIG("linode.atoom.net. 1800 IN RRSIG AAAA 8 3 1800 20170112031301 20161213031301 53289 atoom.net. l+9Qc914zFH/okG2fzJ1q olQ="),
- },
- },
-}
-
-func TestLookupGlue(t *testing.T) {
- zone, err := Parse(strings.NewReader(dbAtoomNetSigned), atoom, "stdin", 0)
- if err != nil {
- t.Fatalf("Expected no error when reading zone, got %q", err)
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{atoom: zone}, Names: []string{atoom}}}
- ctx := context.TODO()
-
- for _, tc := range atoomTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-const dbAtoomNetSigned = `
-; File written on Tue Dec 13 04:13:01 2016
-; dnssec_signzone version 9.10.3-P4-Debian
-atoom.net. 1800 IN SOA linode.atoom.net. miek.miek.nl. (
- 1481602381 ; serial
- 14400 ; refresh (4 hours)
- 3600 ; retry (1 hour)
- 604800 ; expire (1 week)
- 14400 ; minimum (4 hours)
- )
- 1800 RRSIG SOA 8 2 1800 (
- 20170112031301 20161213031301 53289 atoom.net.
- GZ30uFuGATKzwHXgpEwK70qjdXSAqmbB5d4z
- e7WTibvJDPLa1ptZBI7Zuod2KMOkT1ocSvhL
- U7makhdv0BQx+5RSaP25mAmPIzfU7/T7R+DJ
- 5q1GLlDSvOprfyMUlwOgZKZinesSdUa9gRmu
- 8E+XnPNJ/jcTrGzzaDjn1/irrM0= )
- 1800 NS omval.tednet.nl.
- 1800 NS linode.atoom.net.
- 1800 NS ns-ext.nlnetlabs.nl.
- 1800 RRSIG NS 8 2 1800 (
- 20170112031301 20161213031301 53289 atoom.net.
- D8Sd9JpXIOxOrUF5Hi1ASutyQwP7JNu8XZxA
- rse86A6L01O8H8sCNib2VEoJjHuZ/dDEogng
- OgmfqeFy04cpSX19GAk3bkx8Lr6aEat3nqIC
- XA/xsCCfXy0NKZpI05zntHPbbP5tF/NvpE7n
- 0+oLtlHSPEg1ZnEgwNoLe+G1jlw= )
- 1800 A 176.58.119.54
- 1800 RRSIG A 8 2 1800 (
- 20170112031301 20161213031301 53289 atoom.net.
- mrjiUFNCqDgCW8TuhjzcMh0V841uC224QvwH
- 0+OvYhcve9twbX3Y12PSFmz77Xz3Jg9WAj4I
- qhh3iHUac4dzUXyC702DT62yMF/9CMUO0+Ee
- b6wRtvPHr2Tt0i/xV/BTbArInIvurXJrvKvo
- LsZHOfsg7dZs6Mvdpe/CgwRExpk= )
- 1800 AAAA 2a01:7e00::f03c:91ff:fe79:234c
- 1800 RRSIG AAAA 8 2 1800 (
- 20170112031301 20161213031301 53289 atoom.net.
- EkMxX2vUaP4h0qbWlHaT4yNhm8MrPMZTn/3R
- zNw+i3oF2cLMWKh6GCfuIX/x5ID706o8kfum
- bxTYwuTe1LJ+GoZHWEiH8VCa1laTlh8l3qSi
- PZKU8339rr5cCYluk6p9PbAuRkYYOEruNg42
- wPOx46dsAlvp2XpOaOeJtU64QGQ= )
- 14400 NSEC deb.atoom.net. A NS SOA AAAA RRSIG NSEC DNSKEY
- 14400 RRSIG NSEC 8 2 14400 (
- 20170112031301 20161213031301 53289 atoom.net.
- P7Stx7lqRKl8tbTAAaJ0W6UhgJwZz3cjpM8z
- eplbhXEVohKtyJ9xgptKt1vreH6lkhzciar5
- EB9Nj0VOmcthiht/+As8aEKmf8UlcJ2EbLII
- NT7NUaasxsrLE2rjjX5mEtzOZ1uQAGiU8Hnk
- XdGweTgIVFuiCcMCgaKpC2TRrMw= )
- 1800 DNSKEY 256 3 8 (
- AwEAAeDZTH9YT9qLMPlq4VrxX7H3GbWcqCrC
- tXc9RT/hf96GN+ttnnEQVaJY8Gbly3IZpYQW
- MwaCi0t30UULXE3s9FUQtl4AMbplyiz9EF8L
- /XoBS1yhGm5WV5u608ihoPaRkYNyVV3egb5Y
- hA5EXWy2vfsa1XWPpxvSAhlqM0YENtP3
- ) ; ZSK; alg = RSASHA256; key id = 53289
- 1800 DNSKEY 257 3 8 (
- AwEAAepN7Vo8enDCruVduVlGxTDIv7QG0wJQ
- fTL1hMy4k0Yf/7dXzrn5bZT4ytBvH1hoBImH
- mtTrQo6DQlBBVXDJXTyQjQozaHpN1HhTJJTz
- IXl8UrdbkLWvz6QSeJPmBBYQRAqylUA2KE29
- nxyiNboheDLiIWyQ7Q/Op7lYaKMdb555kQAs
- b/XT4Tb3/3BhAjcofNofNBjDjPq2i8pAo8HU
- 5mW5/Pl+ZT/S0aqQPnCkHk/iofSRu3ZdBzkH
- 54eoC+BdyXb7gTbPGRr+1gMbf/rzhRiZ4vnX
- NoEzGAXmorKzJHANNb6KQ/932V9UDHm9wbln
- 6y3s7IBvsMX5KF8vo81Stkc=
- ) ; KSK; alg = RSASHA256; key id = 19114
- 1800 RRSIG DNSKEY 8 2 1800 (
- 20170112031301 20161213031301 19114 atoom.net.
- IEjViubKdef8RWB5bcnirqVcqDk16irkywJZ
- sBjMyNs03/a+sl0UHEGAB7qCC+Rn+RDaM5It
- WF+Gha6BwRIN9NuSg3BwB2h1nJtHw61pMVU9
- 2j9Q3pq7X1xoTBAcwY95t5a1xlw0iTCaLu1L
- Iu/PbVp1gj1o8BF/PiYilvZJGUjaTgsi+YNi
- 2kiWpp6afO78/W4nfVx+lQBmpyfX1lwL5PEC
- 9f5PMbzRmOapvUBc2XdddGywLdmlNsLHimGV
- t7kkHZHOWQR1TvvMbU3dsC0bFCrBVGDhEuxC
- hATR+X5YV0AyDSyrew7fOGJKrapwMWS3yRLr
- FAt0Vcxno5lwQImbCQ== )
- 1800 RRSIG DNSKEY 8 2 1800 (
- 20170112031301 20161213031301 53289 atoom.net.
- sSxdgPT+gFZPN0ot6lZRGqOwvONUEsg0uEbf
- kh19JlWHu/qvq5HOOK2VOW/UnswpVmtpFk0W
- z/jiCNHifjpCCVn5tfCMZDLGekmPOjdobw24
- swBuGjnn0NHvxHoN6S+mb+AR6V/dLjquNUda
- yzBc2Ua+XtQ7SCLKIvEhcNg9H3o= )
-deb.atoom.net. 1800 IN A 176.58.119.54
- 1800 RRSIG A 8 3 1800 (
- 20170112031301 20161213031301 53289 atoom.net.
- ZW7jm/VDa/I9DxWlE7Cm+HHymiVv4Wk5UGYI
- Uf/g0EfxLCBR6SwL5QKuV1z7xoWKaiNqqrmc
- gg35xgskKyS8QHgCCODhDzcIKe+MSsBXbY04
- AtrC5dV3JJQoA65Ng/48hwcyghAjXKrA2Yyq
- GXf2DSvWeIV9Jmk0CsOELP24dpk= )
- 1800 TXT "v=spf1 a ip6:2a01:7e00::f03c:91ff:fe79:234c ~all"
- 1800 RRSIG TXT 8 3 1800 (
- 20170112031301 20161213031301 53289 atoom.net.
- fpvVJ+Z6tzSd9yETn/PhLSCRISwRD1c3ET80
- 8twnx3XfAPQfV2R8dw7pz8Vw4TSxvf19bAZc
- PWRjW682gb7gAxoJshCXBYabMfqExrBc9V1S
- ezwm3D93xNMyegxzHx2b/H8qp3ZWdsMLTvvN
- Azu7P4iyO+WRWT0R7bJGrdTwRz8= )
- 1800 AAAA 2a01:7e00::f03c:91ff:fe79:234c
- 1800 RRSIG AAAA 8 3 1800 (
- 20170112031301 20161213031301 53289 atoom.net.
- aaPF6NqXfWamzi+xUDVeYa7StJUVM1tDsL34
- w5uozFRZ0f4K/Z88Kk5CgztxmtpNNKGdLWa0
- iryUJsbVWAbSQfrZNkNckBtczMNxGgjqn97A
- 2//F6ajH/qrR3dWcCm+VJMgu3UPqAxLiCaYO
- GQUx6Y8JA1VIM/RJAM6BhgNxjD0= )
- 14400 NSEC lafhart.atoom.net. A TXT AAAA RRSIG NSEC
- 14400 RRSIG NSEC 8 3 14400 (
- 20170112031301 20161213031301 53289 atoom.net.
- 1Llad64NDWcz8CyBu2TsyANrJ9Tpfm5257sY
- FPYF579p3c9Imwp9kYEO1zMEKgNoXBN/sQnd
- YCugq3r2GAI6bfJj8sV5bt6GKuZcGHMESug4
- uh2gU0NDcCA4GPdBYGdusePwV0RNpcRnVCFA
- fsACp+22j3uwRUbCh0re0ufbAs4= )
-lafhart.atoom.net. 1800 IN A 178.79.160.171
- 1800 RRSIG A 8 3 1800 (
- 20170112031301 20161213031301 53289 atoom.net.
- fruP6cvMVICXEV8NcheS73NWLCEKlO1FgW6B
- 35D2GhtfYZe+M23V5YBRtlVCCrAdS0etdCOf
- xH9yt3u2kVvDXuMRiQr1zJPRDEq3cScYumpd
- bOO8cjHiCic5lEcRVWNNHXyGtpqTvrp9CxOu
- IQw1WgAlZyKj43zGg3WZi6OTKLg= )
- 14400 NSEC linode.atoom.net. A RRSIG NSEC
- 14400 RRSIG NSEC 8 3 14400 (
- 20170112031301 20161213031301 53289 atoom.net.
- 2AUWXbScL0jIJ7G6UsJAlUs+bgSprZ1zY6v/
- iVB5BAYwZD6pPky7LZdzvPEHh0aNLGIFbbU8
- SDJI7u/e4RUTlE+8yyjl6obZNfNKyJFqE5xN
- 1BJ8sjFrVn6KaHIDKEOZunNb1MlMfCRkLg9O
- 94zg04XEgVUfaYCPxvLs3fCEgzw= )
-voordeur.atoom.net. 1800 IN A 77.249.87.46
- 1800 RRSIG A 8 3 1800 (
- 20170112031301 20161213031301 53289 atoom.net.
- SzJz0NaKLRA/lW4CxgMHgeuQLp5QqFEjQv3I
- zfPtY4joQsZn8RN8RLECcpcPKjbC8Dj6mxIJ
- dd2vwhsCVlZKMNcZUOfpB7eGx1TR9HnzMkY9
- OdTt30a9+tktagrJEoy31vAhj1hJqLbSgvOa
- pRr1P4ZpQ53/qH8JX/LOmqfWTdg= )
- 14400 NSEC www.atoom.net. A RRSIG NSEC
- 14400 RRSIG NSEC 8 3 14400 (
- 20170112031301 20161213031301 53289 atoom.net.
- CETJhUJy1rKjVj9wsW1549gth+/Z37//BI6S
- nxJ+2Oq63jEjlbznmyo5hvFW54DbVUod+cLo
- N9PdlNQDr1XsRBgWhkKW37RkuoRVEPwqRykv
- xzn9i7CgYKAAHFyWMGihBLkV9ByPp8GDR8Zr
- DEkrG3ErDlBcwi3FqGZFsSOW2xg= )
-www.atoom.net. 1800 IN CNAME deb.atoom.net.
- 1800 RRSIG CNAME 8 3 1800 (
- 20170112031301 20161213031301 53289 atoom.net.
- 1lhG6iTtbeesBCVOrA8a7+V2gogCuXzKgSi8
- 6K0Pzq2CwqTScdNcZvcDOIbLq45Am5p09PIj
- lXnd2fw6WAxphwvRhmwCve3uTZMUt5STw7oi
- 0rED7GMuFUSC/BX0XVly7NET3ECa1vaK6RhO
- hDSsKPWFI7to4d1z6tQ9j9Kvm4Y= )
- 14400 NSEC atoom.net. CNAME RRSIG NSEC
- 14400 RRSIG NSEC 8 3 14400 (
- 20170112031301 20161213031301 53289 atoom.net.
- CC4yCYP1q75/gTmPz+mVM6Lam2foPP5oTccY
- RtROuTkgbt8DtAoPe304vmNazWBlGidnWJeD
- YyAAe3znIHP0CgrxjD/hRL9FUzMnVrvB3mnx
- 4W13wP1rE97RqJxV1kk22Wl3uCkVGy7LCjb0
- JLFvzCe2fuMe7YcTzI+t1rioTP0= )
-linode.atoom.net. 1800 IN A 176.58.119.54
- 1800 RRSIG A 8 3 1800 (
- 20170112031301 20161213031301 53289 atoom.net.
- Z4Ka4OLDha4eQNWs3GtUd1Cumr48RUnH523I
- nZzGXtpQNou70qsm5Jt8n/HmsZ4L5DoxomRz
- rgZTGnrqj43+A16UUGfVEk6SfUUHOgxgspQW
- zoaqk5/5mQO1ROsLKY8RqaRqzvbToHvqeZEh
- VkTPVA02JK9UFlKqoyxj72CLvkI= )
- 1800 AAAA 2a01:7e00::f03c:91ff:fe79:234c
- 1800 RRSIG AAAA 8 3 1800 (
- 20170112031301 20161213031301 53289 atoom.net.
- l+9Qce/EQyKrTJVKLv7iatjuCO285ckd5Oie
- P2LzWVsL4tW04oHzieKZwIuNBRE+px8g5qrT
- LIK2TikCGL1xHAd7CT7gbCtDcZ7jHmSTmMTJ
- 405nOV3G3xWelreLI5Fn5ck8noEsF64kiw1y
- XfkyQn2B914zFH/okG2fzJ1qolQ= )
- 14400 NSEC voordeur.atoom.net. A AAAA RRSIG NSEC
- 14400 RRSIG NSEC 8 3 14400 (
- 20170112031301 20161213031301 53289 atoom.net.
- Owzmz7QrVL2Gw2njEsUVEknMl2amx1HG9X3K
- tO+Ihyy4tApiUFxUjAu3P/30QdqbB85h7s//
- ipwX/AmQJNoxTScR3nHt9qDqJ044DPmiuh0l
- NuIjguyZRANApmKCTA6AoxXIUqToIIjfVzi/
- PxXE6T3YIPlK7Bxgv1lcCBJ1fmE= )`
-
-const atoom = "atoom.net."
diff --git a/middleware/file/include_test.go b/middleware/file/include_test.go
deleted file mode 100644
index f07a02e03..000000000
--- a/middleware/file/include_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package file
-
-import (
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/test"
-)
-
-// Make sure the external miekg/dns dependency is up to date
-
-func TestInclude(t *testing.T) {
-
- name, rm, err := test.TempFile(".", "foo\tIN\tA\t127.0.0.1\n")
- if err != nil {
- t.Fatalf("Unable to create tmpfile %q: %s", name, err)
- }
- defer rm()
-
- zone := `$ORIGIN example.org.
-@ IN SOA sns.dns.icann.org. noc.dns.icann.org. 2017042766 7200 3600 1209600 3600
-$INCLUDE ` + name + "\n"
-
- z, err := Parse(strings.NewReader(zone), "example.org.", "test", 0)
- if err != nil {
- t.Errorf("Unable to parse zone %q: %s", "example.org.", err)
- }
-
- if _, ok := z.Search("foo.example.org."); !ok {
- t.Errorf("Failed to find %q in parsed zone", "foo.example.org.")
- }
-}
diff --git a/middleware/file/lookup.go b/middleware/file/lookup.go
deleted file mode 100644
index 89e0f5a47..000000000
--- a/middleware/file/lookup.go
+++ /dev/null
@@ -1,467 +0,0 @@
-package file
-
-import (
- "github.com/coredns/coredns/middleware/file/tree"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-// Result is the result of a Lookup
-type Result int
-
-const (
- // Success is a successful lookup.
- Success Result = iota
- // NameError indicates a nameerror
- NameError
- // Delegation indicates the lookup resulted in a delegation.
- Delegation
- // NoData indicates the lookup resulted in a NODATA.
- NoData
- // ServerFailure indicates a server failure during the lookup.
- ServerFailure
-)
-
-// Lookup looks up qname and qtype in the zone. When do is true DNSSEC records are included.
-// Three sets of records are returned, one for the answer, one for authority and one for the additional section.
-func (z *Zone) Lookup(state request.Request, qname string) ([]dns.RR, []dns.RR, []dns.RR, Result) {
-
- qtype := state.QType()
- do := state.Do()
-
- if !z.NoReload {
- z.reloadMu.RLock()
- }
- defer func() {
- if !z.NoReload {
- z.reloadMu.RUnlock()
- }
- }()
-
- // If z is a secondary zone we might not have transferred it, meaning we have
- // all zone context setup, except the actual record. This means (for one thing) the apex
- // is empty and we don't have a SOA record.
- soa := z.Apex.SOA
- if soa == nil {
- return nil, nil, nil, ServerFailure
- }
-
- if qtype == dns.TypeSOA {
- return z.soa(do), z.ns(do), nil, Success
- }
- if qtype == dns.TypeNS && qname == z.origin {
- nsrrs := z.ns(do)
- glue := z.Glue(nsrrs, do)
- return nsrrs, nil, glue, Success
- }
-
- var (
- found, shot bool
- parts string
- i int
- elem, wildElem *tree.Elem
- )
-
- // Lookup:
- // * Per label from the right, look if it exists. We do this to find potential
- // delegation records.
- // * If the per-label search finds nothing, we will look for the wildcard at the
- // level. If found we keep it around. If we don't find the complete name we will
- // use the wildcard.
- //
- // Main for-loop handles delegation and finding or not finding the qname.
- // If found we check if it is a CNAME/DNAME and do CNAME processing
- // We also check if we have type and do a nodata resposne.
- //
- // If not found, we check the potential wildcard, and use that for further processing.
- // If not found and no wildcard we will process this as an NXDOMAIN response.
- for {
- parts, shot = z.nameFromRight(qname, i)
- // We overshot the name, break and check if we previously found something.
- if shot {
- break
- }
-
- elem, found = z.Tree.Search(parts)
- if !found {
- // Apex will always be found, when we are here we can search for a wildcard
- // and save the result of that search. So when nothing match, but we have a
- // wildcard we should expand the wildcard.
-
- wildcard := replaceWithAsteriskLabel(parts)
- if wild, found := z.Tree.Search(wildcard); found {
- wildElem = wild
- }
-
- // Keep on searching, because maybe we hit an empty-non-terminal (which aren't
- // stored in the tree. Only when we have match the full qname (and possible wildcard
- // we can be confident that we didn't find anything.
- i++
- continue
- }
-
- // If we see DNAME records, we should return those.
- if dnamerrs := elem.Types(dns.TypeDNAME); dnamerrs != nil {
- // Only one DNAME is allowed per name. We just pick the first one to synthesize from.
- dname := dnamerrs[0]
- if cname := synthesizeCNAME(state.Name(), dname.(*dns.DNAME)); cname != nil {
- answer, ns, extra, rcode := z.searchCNAME(state, elem, []dns.RR{cname})
-
- if do {
- sigs := elem.Types(dns.TypeRRSIG)
- sigs = signatureForSubType(sigs, dns.TypeDNAME)
- dnamerrs = append(dnamerrs, sigs...)
- }
-
- // The relevant DNAME RR should be included in the answer section,
- // if the DNAME is being employed as a substitution instruction.
- answer = append(dnamerrs, answer...)
-
- return answer, ns, extra, rcode
- }
- // The domain name that owns a DNAME record is allowed to have other RR types
- // at that domain name, except those have restrictions on what they can coexist
- // with (e.g. another DNAME). So there is nothing special left here.
- }
-
- // If we see NS records, it means the name as been delegated, and we should return the delegation.
- if nsrrs := elem.Types(dns.TypeNS); nsrrs != nil {
- glue := z.Glue(nsrrs, do)
- // If qtype == NS, we should returns success to put RRs in answer.
- if qtype == dns.TypeNS {
- return nsrrs, nil, glue, Success
- }
-
- if do {
- dss := z.typeFromElem(elem, dns.TypeDS, do)
- nsrrs = append(nsrrs, dss...)
- }
-
- return nil, nsrrs, glue, Delegation
- }
-
- i++
- }
-
- // What does found and !shot mean - do we ever hit it?
- if found && !shot {
- return nil, nil, nil, ServerFailure
- }
-
- // Found entire name.
- if found && shot {
-
- if rrs := elem.Types(dns.TypeCNAME); len(rrs) > 0 && qtype != dns.TypeCNAME {
- return z.searchCNAME(state, elem, rrs)
- }
-
- rrs := elem.Types(qtype, qname)
-
- // NODATA
- if len(rrs) == 0 {
- ret := z.soa(do)
- if do {
- nsec := z.typeFromElem(elem, dns.TypeNSEC, do)
- ret = append(ret, nsec...)
- }
- return nil, ret, nil, NoData
- }
-
- // Additional section processing for MX, SRV. Check response and see if any of the names are in baliwick -
- // if so add IP addresses to the additional section.
- additional := additionalProcessing(z, rrs, do)
-
- if do {
- sigs := elem.Types(dns.TypeRRSIG)
- sigs = signatureForSubType(sigs, qtype)
- rrs = append(rrs, sigs...)
- }
-
- return rrs, z.ns(do), additional, Success
-
- }
-
- // Haven't found the original name.
-
- // Found wildcard.
- if wildElem != nil {
- auth := z.ns(do)
-
- if rrs := wildElem.Types(dns.TypeCNAME, qname); len(rrs) > 0 {
- return z.searchCNAME(state, wildElem, rrs)
- }
-
- rrs := wildElem.Types(qtype, qname)
-
- // NODATA response.
- if len(rrs) == 0 {
- ret := z.soa(do)
- if do {
- nsec := z.typeFromElem(wildElem, dns.TypeNSEC, do)
- ret = append(ret, nsec...)
- }
- return nil, ret, nil, Success
- }
-
- if do {
- // An NSEC is needed to say no longer name exists under this wildcard.
- if deny, found := z.Tree.Prev(qname); found {
- nsec := z.typeFromElem(deny, dns.TypeNSEC, do)
- auth = append(auth, nsec...)
- }
-
- sigs := wildElem.Types(dns.TypeRRSIG, qname)
- sigs = signatureForSubType(sigs, qtype)
- rrs = append(rrs, sigs...)
-
- }
- return rrs, auth, nil, Success
- }
-
- rcode := NameError
-
- // Hacky way to get around empty-non-terminals. If a longer name does exist, but this qname, does not, it
- // must be an empty-non-terminal. If so, we do the proper NXDOMAIN handling, but set the rcode to be success.
- if x, found := z.Tree.Next(qname); found {
- if dns.IsSubDomain(qname, x.Name()) {
- rcode = Success
- }
- }
-
- ret := z.soa(do)
- if do {
- deny, _ := z.Tree.Prev(qname) // TODO(miek): *found* was not used here.
- nsec := z.typeFromElem(deny, dns.TypeNSEC, do)
- ret = append(ret, nsec...)
-
- if rcode != NameError {
- goto Out
- }
-
- ce, found := z.ClosestEncloser(qname)
-
- // wildcard denial only for NXDOMAIN
- if found {
- // wildcard denial
- wildcard := "*." + ce.Name()
- if ss, found := z.Tree.Prev(wildcard); found {
- // Only add this nsec if it is different than the one already added
- if ss.Name() != deny.Name() {
- nsec := z.typeFromElem(ss, dns.TypeNSEC, do)
- ret = append(ret, nsec...)
- }
- }
- }
-
- }
-Out:
- return nil, ret, nil, rcode
-}
-
-// Return type tp from e and add signatures (if they exists) and do is true.
-func (z *Zone) typeFromElem(elem *tree.Elem, tp uint16, do bool) []dns.RR {
- rrs := elem.Types(tp)
- if do {
- sigs := elem.Types(dns.TypeRRSIG)
- sigs = signatureForSubType(sigs, tp)
- if len(sigs) > 0 {
- rrs = append(rrs, sigs...)
- }
- }
- return rrs
-}
-
-func (z *Zone) soa(do bool) []dns.RR {
- if do {
- ret := append([]dns.RR{z.Apex.SOA}, z.Apex.SIGSOA...)
- return ret
- }
- return []dns.RR{z.Apex.SOA}
-}
-
-func (z *Zone) ns(do bool) []dns.RR {
- if do {
- ret := append(z.Apex.NS, z.Apex.SIGNS...)
- return ret
- }
- return z.Apex.NS
-}
-
-// TODO(miek): should be better named, like aditionalProcessing?
-func (z *Zone) searchCNAME(state request.Request, elem *tree.Elem, rrs []dns.RR) ([]dns.RR, []dns.RR, []dns.RR, Result) {
-
- qtype := state.QType()
- do := state.Do()
-
- if do {
- sigs := elem.Types(dns.TypeRRSIG)
- sigs = signatureForSubType(sigs, dns.TypeCNAME)
- if len(sigs) > 0 {
- rrs = append(rrs, sigs...)
- }
- }
-
- targetName := rrs[0].(*dns.CNAME).Target
- elem, _ = z.Tree.Search(targetName)
- if elem == nil {
- if !dns.IsSubDomain(z.origin, targetName) {
- rrs = append(rrs, z.externalLookup(state, targetName, qtype)...)
- }
- return rrs, z.ns(do), nil, Success
- }
-
- i := 0
-
-Redo:
- cname := elem.Types(dns.TypeCNAME)
- if len(cname) > 0 {
- rrs = append(rrs, cname...)
-
- if do {
- sigs := elem.Types(dns.TypeRRSIG)
- sigs = signatureForSubType(sigs, dns.TypeCNAME)
- if len(sigs) > 0 {
- rrs = append(rrs, sigs...)
- }
- }
- targetName := cname[0].(*dns.CNAME).Target
- elem, _ = z.Tree.Search(targetName)
- if elem == nil {
- if !dns.IsSubDomain(z.origin, targetName) {
- if !dns.IsSubDomain(z.origin, targetName) {
- rrs = append(rrs, z.externalLookup(state, targetName, qtype)...)
- }
- }
- return rrs, z.ns(do), nil, Success
- }
-
- i++
- if i > maxChain {
- return rrs, z.ns(do), nil, Success
- }
-
- goto Redo
- }
-
- targets := cnameForType(elem.All(), qtype)
- if len(targets) > 0 {
- rrs = append(rrs, targets...)
-
- if do {
- sigs := elem.Types(dns.TypeRRSIG)
- sigs = signatureForSubType(sigs, qtype)
- if len(sigs) > 0 {
- rrs = append(rrs, sigs...)
- }
- }
- }
-
- return rrs, z.ns(do), nil, Success
-}
-
-func cnameForType(targets []dns.RR, origQtype uint16) []dns.RR {
- ret := []dns.RR{}
- for _, target := range targets {
- if target.Header().Rrtype == origQtype {
- ret = append(ret, target)
- }
- }
- return ret
-}
-
-func (z *Zone) externalLookup(state request.Request, target string, qtype uint16) []dns.RR {
- m, e := z.Proxy.Lookup(state, target, qtype)
- if e != nil {
- // TODO(miek): debugMsg for this as well? Log?
- return nil
- }
- return m.Answer
-}
-
-// signatureForSubType range through the signature and return the correct ones for the subtype.
-func signatureForSubType(rrs []dns.RR, subtype uint16) []dns.RR {
- sigs := []dns.RR{}
- for _, sig := range rrs {
- if s, ok := sig.(*dns.RRSIG); ok {
- if s.TypeCovered == subtype {
- sigs = append(sigs, s)
- }
- }
- }
- return sigs
-}
-
-// Glue returns any potential glue records for nsrrs.
-func (z *Zone) Glue(nsrrs []dns.RR, do bool) []dns.RR {
- glue := []dns.RR{}
- for _, rr := range nsrrs {
- if ns, ok := rr.(*dns.NS); ok && dns.IsSubDomain(ns.Header().Name, ns.Ns) {
- glue = append(glue, z.searchGlue(ns.Ns, do)...)
- }
- }
- return glue
-}
-
-// searchGlue looks up A and AAAA for name.
-func (z *Zone) searchGlue(name string, do bool) []dns.RR {
- glue := []dns.RR{}
-
- // A
- if elem, found := z.Tree.Search(name); found {
- glue = append(glue, elem.Types(dns.TypeA)...)
- if do {
- sigs := elem.Types(dns.TypeRRSIG)
- sigs = signatureForSubType(sigs, dns.TypeA)
- glue = append(glue, sigs...)
- }
- }
-
- // AAAA
- if elem, found := z.Tree.Search(name); found {
- glue = append(glue, elem.Types(dns.TypeAAAA)...)
- if do {
- sigs := elem.Types(dns.TypeRRSIG)
- sigs = signatureForSubType(sigs, dns.TypeAAAA)
- glue = append(glue, sigs...)
- }
- }
- return glue
-}
-
-// additionalProcessing checks the current answer section and retrieves A or AAAA records
-// (and possible SIGs) to need to be put in the additional section.
-func additionalProcessing(z *Zone, answer []dns.RR, do bool) (extra []dns.RR) {
- for _, rr := range answer {
- name := ""
- switch x := rr.(type) {
- case *dns.SRV:
- name = x.Target
- case *dns.MX:
- name = x.Mx
- }
- if !dns.IsSubDomain(z.origin, name) {
- continue
- }
-
- elem, _ := z.Tree.Search(name)
- if elem == nil {
- continue
- }
-
- sigs := elem.Types(dns.TypeRRSIG)
- for _, addr := range []uint16{dns.TypeA, dns.TypeAAAA} {
- if a := elem.Types(addr); a != nil {
- extra = append(extra, a...)
- if do {
- sig := signatureForSubType(sigs, addr)
- extra = append(extra, sig...)
- }
- }
- }
- }
-
- return extra
-}
-
-const maxChain = 8
diff --git a/middleware/file/lookup_test.go b/middleware/file/lookup_test.go
deleted file mode 100644
index 82d8f77ff..000000000
--- a/middleware/file/lookup_test.go
+++ /dev/null
@@ -1,194 +0,0 @@
-package file
-
-import (
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-var dnsTestCases = []test.Case{
- {
- Qname: "www.miek.nl.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.A("a.miek.nl. 1800 IN A 139.162.196.78"),
- test.CNAME("www.miek.nl. 1800 IN CNAME a.miek.nl."),
- },
- Ns: miekAuth,
- },
- {
- Qname: "www.miek.nl.", Qtype: dns.TypeAAAA,
- Answer: []dns.RR{
- test.AAAA("a.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- test.CNAME("www.miek.nl. 1800 IN CNAME a.miek.nl."),
- },
- Ns: miekAuth,
- },
- {
- Qname: "miek.nl.", Qtype: dns.TypeSOA,
- Answer: []dns.RR{
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- Ns: miekAuth,
- },
- {
- Qname: "miek.nl.", Qtype: dns.TypeAAAA,
- Answer: []dns.RR{
- test.AAAA("miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- Ns: miekAuth,
- },
- {
- Qname: "mIeK.NL.", Qtype: dns.TypeAAAA,
- Answer: []dns.RR{
- test.AAAA("miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- Ns: miekAuth,
- },
- {
- Qname: "miek.nl.", Qtype: dns.TypeMX,
- Answer: []dns.RR{
- test.MX("miek.nl. 1800 IN MX 1 aspmx.l.google.com."),
- test.MX("miek.nl. 1800 IN MX 10 aspmx2.googlemail.com."),
- test.MX("miek.nl. 1800 IN MX 10 aspmx3.googlemail.com."),
- test.MX("miek.nl. 1800 IN MX 5 alt1.aspmx.l.google.com."),
- test.MX("miek.nl. 1800 IN MX 5 alt2.aspmx.l.google.com."),
- },
- Ns: miekAuth,
- },
- {
- Qname: "a.miek.nl.", Qtype: dns.TypeSRV,
- Ns: []dns.RR{
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- },
- {
- Qname: "b.miek.nl.", Qtype: dns.TypeA,
- Rcode: dns.RcodeNameError,
- Ns: []dns.RR{
- test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
- },
- },
- {
- Qname: "srv.miek.nl.", Qtype: dns.TypeSRV,
- Answer: []dns.RR{
- test.SRV("srv.miek.nl. 1800 IN SRV 10 10 8080 a.miek.nl."),
- },
- Extra: []dns.RR{
- test.A("a.miek.nl. 1800 IN A 139.162.196.78"),
- test.AAAA("a.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- Ns: miekAuth,
- },
- {
- Qname: "mx.miek.nl.", Qtype: dns.TypeMX,
- Answer: []dns.RR{
- test.MX("mx.miek.nl. 1800 IN MX 10 a.miek.nl."),
- },
- Extra: []dns.RR{
- test.A("a.miek.nl. 1800 IN A 139.162.196.78"),
- test.AAAA("a.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
- },
- Ns: miekAuth,
- },
-}
-
-const (
- testzone = "miek.nl."
- testzone1 = "dnssex.nl."
-)
-
-func TestLookup(t *testing.T) {
- zone, err := Parse(strings.NewReader(dbMiekNL), testzone, "stdin", 0)
- if err != nil {
- t.Fatalf("expect no error when reading zone, got %q", err)
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{testzone: zone}, Names: []string{testzone}}}
- ctx := context.TODO()
-
- for _, tc := range dnsTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-func TestLookupNil(t *testing.T) {
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{testzone: nil}, Names: []string{testzone}}}
- ctx := context.TODO()
-
- m := dnsTestCases[0].Msg()
- rec := dnsrecorder.New(&test.ResponseWriter{})
- fm.ServeDNS(ctx, rec, m)
-}
-
-func BenchmarkFileLookup(b *testing.B) {
- zone, err := Parse(strings.NewReader(dbMiekNL), testzone, "stdin", 0)
- if err != nil {
- return
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{testzone: zone}, Names: []string{testzone}}}
- ctx := context.TODO()
- rec := dnsrecorder.New(&test.ResponseWriter{})
-
- tc := test.Case{
- Qname: "www.miek.nl.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.CNAME("www.miek.nl. 1800 IN CNAME a.miek.nl."),
- test.A("a.miek.nl. 1800 IN A 139.162.196.78"),
- },
- }
-
- m := tc.Msg()
-
- b.ResetTimer()
-
- for i := 0; i < b.N; i++ {
- fm.ServeDNS(ctx, rec, m)
- }
-}
-
-const dbMiekNL = `
-$TTL 30M
-$ORIGIN miek.nl.
-@ IN SOA linode.atoom.net. miek.miek.nl. (
- 1282630057 ; Serial
- 4H ; Refresh
- 1H ; Retry
- 7D ; Expire
- 4H ) ; Negative Cache TTL
- IN NS linode.atoom.net.
- IN NS ns-ext.nlnetlabs.nl.
- IN NS omval.tednet.nl.
- IN NS ext.ns.whyscream.net.
-
- IN MX 1 aspmx.l.google.com.
- IN MX 5 alt1.aspmx.l.google.com.
- IN MX 5 alt2.aspmx.l.google.com.
- IN MX 10 aspmx2.googlemail.com.
- IN MX 10 aspmx3.googlemail.com.
-
- IN A 139.162.196.78
- IN AAAA 2a01:7e00::f03c:91ff:fef1:6735
-
-a IN A 139.162.196.78
- IN AAAA 2a01:7e00::f03c:91ff:fef1:6735
-www IN CNAME a
-archive IN CNAME a
-
-srv IN SRV 10 10 8080 a.miek.nl.
-mx IN MX 10 a.miek.nl.`
diff --git a/middleware/file/notify.go b/middleware/file/notify.go
deleted file mode 100644
index b1628c3a6..000000000
--- a/middleware/file/notify.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package file
-
-import (
- "fmt"
- "log"
- "net"
-
- "github.com/coredns/coredns/middleware/pkg/rcode"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-// isNotify checks if state is a notify message and if so, will *also* check if it
-// is from one of the configured masters. If not it will not be a valid notify
-// message. If the zone z is not a secondary zone the message will also be ignored.
-func (z *Zone) isNotify(state request.Request) bool {
- if state.Req.Opcode != dns.OpcodeNotify {
- return false
- }
- if len(z.TransferFrom) == 0 {
- return false
- }
- // If remote IP matches we accept.
- remote := state.IP()
- for _, f := range z.TransferFrom {
- from, _, err := net.SplitHostPort(f)
- if err != nil {
- continue
- }
- if from == remote {
- return true
- }
- }
- return false
-}
-
-// Notify will send notifies to all configured TransferTo IP addresses.
-func (z *Zone) Notify() {
- go notify(z.origin, z.TransferTo)
-}
-
-// notify sends notifies to the configured remote servers. It will try up to three times
-// before giving up on a specific remote. We will sequentially loop through "to"
-// until they all have replied (or have 3 failed attempts).
-func notify(zone string, to []string) error {
- m := new(dns.Msg)
- m.SetNotify(zone)
- c := new(dns.Client)
-
- for _, t := range to {
- if t == "*" {
- continue
- }
- if err := notifyAddr(c, m, t); err != nil {
- log.Printf("[ERROR] " + err.Error())
- } else {
- log.Printf("[INFO] Sent notify for zone %q to %q", zone, t)
- }
- }
- return nil
-}
-
-func notifyAddr(c *dns.Client, m *dns.Msg, s string) error {
- var err error
-
- code := dns.RcodeServerFailure
- for i := 0; i < 3; i++ {
- ret, _, err := c.Exchange(m, s)
- if err != nil {
- continue
- }
- code = ret.Rcode
- if code == dns.RcodeSuccess {
- return nil
- }
- }
- if err != nil {
- return fmt.Errorf("notify for zone %q was not accepted by %q: %q", m.Question[0].Name, s, err)
- }
- return fmt.Errorf("notify for zone %q was not accepted by %q: rcode was %q", m.Question[0].Name, s, rcode.ToString(code))
-}
diff --git a/middleware/file/nsec3_test.go b/middleware/file/nsec3_test.go
deleted file mode 100644
index 6611056cb..000000000
--- a/middleware/file/nsec3_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package file
-
-import (
- "strings"
- "testing"
-)
-
-func TestParseNSEC3PARAM(t *testing.T) {
- _, err := Parse(strings.NewReader(nsec3paramTest), "miek.nl", "stdin", 0)
- if err == nil {
- t.Fatalf("expected error when reading zone, got nothing")
- }
-}
-
-func TestParseNSEC3(t *testing.T) {
- _, err := Parse(strings.NewReader(nsec3Test), "miek.nl", "stdin", 0)
- if err == nil {
- t.Fatalf("expected error when reading zone, got nothing")
- }
-}
-
-const nsec3paramTest = `miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1460175181 14400 3600 604800 14400
-miek.nl. 1800 IN NS omval.tednet.nl.
-miek.nl. 0 IN NSEC3PARAM 1 0 5 A3DEBC9CC4F695C7`
-
-const nsec3Test = `example.org. 1800 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2016082508 7200 3600 1209600 3600
-aub8v9ce95ie18spjubsr058h41n7pa5.example.org. 284 IN NSEC3 1 1 5 D0CBEAAF0AC77314 AUB95P93VPKP55G6U5S4SGS7LS61ND85 NS SOA TXT RRSIG DNSKEY NSEC3PARAM
-aub8v9ce95ie18spjubsr058h41n7pa5.example.org. 284 IN RRSIG NSEC3 8 2 600 20160910232502 20160827231002 14028 example.org. XBNpA7KAIjorPbXvTinOHrc1f630aHic2U716GHLHA4QMx9cl9ss4QjR Wj2UpDM9zBW/jNYb1xb0yjQoez/Jv200w0taSWjRci5aUnRpOi9bmcrz STHb6wIUjUsbJ+NstQsUwVkj6679UviF1FqNwr4GlJnWG3ZrhYhE+NI6 s0k=`
diff --git a/middleware/file/reload.go b/middleware/file/reload.go
deleted file mode 100644
index 18e949a94..000000000
--- a/middleware/file/reload.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package file
-
-import (
- "log"
- "os"
- "path"
-
- "github.com/fsnotify/fsnotify"
-)
-
-// Reload reloads a zone when it is changed on disk. If z.NoRoload is true, no reloading will be done.
-func (z *Zone) Reload() error {
- if z.NoReload {
- return nil
- }
- watcher, err := fsnotify.NewWatcher()
- if err != nil {
- return err
- }
- err = watcher.Add(path.Dir(z.file))
- if err != nil {
- return err
- }
-
- go func() {
- // TODO(miek): needs to be killed on reload.
- for {
- select {
- case event := <-watcher.Events:
- if path.Clean(event.Name) == z.file {
-
- reader, err := os.Open(z.file)
- if err != nil {
- log.Printf("[ERROR] Failed to open `%s' for `%s': %v", z.file, z.origin, err)
- continue
- }
-
- serial := z.SOASerialIfDefined()
- zone, err := Parse(reader, z.origin, z.file, serial)
- if err != nil {
- log.Printf("[WARNING] Parsing zone `%s': %v", z.origin, err)
- continue
- }
-
- // copy elements we need
- z.reloadMu.Lock()
- z.Apex = zone.Apex
- z.Tree = zone.Tree
- z.reloadMu.Unlock()
-
- log.Printf("[INFO] Successfully reloaded zone `%s'", z.origin)
- z.Notify()
- }
- case <-z.ReloadShutdown:
- watcher.Close()
- return
- }
- }
- }()
- return nil
-}
-
-// SOASerialIfDefined returns the SOA's serial if the zone has a SOA record in the Apex, or
-// -1 otherwise.
-func (z *Zone) SOASerialIfDefined() int64 {
- z.reloadMu.Lock()
- defer z.reloadMu.Unlock()
- if z.Apex.SOA != nil {
- return int64(z.Apex.SOA.Serial)
- }
- return -1
-}
diff --git a/middleware/file/reload_test.go b/middleware/file/reload_test.go
deleted file mode 100644
index c4d065155..000000000
--- a/middleware/file/reload_test.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package file
-
-import (
- "io/ioutil"
- "log"
- "os"
- "strings"
- "testing"
- "time"
-
- "github.com/coredns/coredns/middleware/test"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-func TestZoneReload(t *testing.T) {
- log.SetOutput(ioutil.Discard)
-
- fileName, rm, err := test.TempFile(".", reloadZoneTest)
- if err != nil {
- t.Fatalf("failed to create zone: %s", err)
- }
- defer rm()
- reader, err := os.Open(fileName)
- if err != nil {
- t.Fatalf("failed to open zone: %s", err)
- }
- z, err := Parse(reader, "miek.nl", fileName, 0)
- if err != nil {
- t.Fatalf("failed to parse zone: %s", err)
- }
-
- z.Reload()
-
- r := new(dns.Msg)
- r.SetQuestion("miek.nl", dns.TypeSOA)
- state := request.Request{W: &test.ResponseWriter{}, Req: r}
- if _, _, _, res := z.Lookup(state, "miek.nl."); res != Success {
- t.Fatalf("failed to lookup, got %d", res)
- }
-
- r = new(dns.Msg)
- r.SetQuestion("miek.nl", dns.TypeNS)
- state = request.Request{W: &test.ResponseWriter{}, Req: r}
- if _, _, _, res := z.Lookup(state, "miek.nl."); res != Success {
- t.Fatalf("failed to lookup, got %d", res)
- }
-
- if len(z.All()) != 5 {
- t.Fatalf("expected 5 RRs, got %d", len(z.All()))
- }
- if err := ioutil.WriteFile(fileName, []byte(reloadZone2Test), 0644); err != nil {
- t.Fatalf("failed to write new zone data: %s", err)
- }
- // Could still be racy, but we need to wait a bit for the event to be seen
- time.Sleep(1 * time.Second)
-
- if len(z.All()) != 3 {
- t.Fatalf("expected 3 RRs, got %d", len(z.All()))
- }
-}
-
-func TestZoneReloadSOAChange(t *testing.T) {
- _, err := Parse(strings.NewReader(reloadZoneTest), "miek.nl.", "stdin", 1460175181)
- if err == nil {
- t.Fatalf("zone should not have been re-parsed")
- }
-
-}
-
-const reloadZoneTest = `miek.nl. 1627 IN SOA linode.atoom.net. miek.miek.nl. 1460175181 14400 3600 604800 14400
-miek.nl. 1627 IN NS ext.ns.whyscream.net.
-miek.nl. 1627 IN NS omval.tednet.nl.
-miek.nl. 1627 IN NS linode.atoom.net.
-miek.nl. 1627 IN NS ns-ext.nlnetlabs.nl.
-`
-
-const reloadZone2Test = `miek.nl. 1627 IN SOA linode.atoom.net. miek.miek.nl. 1460175182 14400 3600 604800 14400
-miek.nl. 1627 IN NS ext.ns.whyscream.net.
-miek.nl. 1627 IN NS omval.tednet.nl.
-`
diff --git a/middleware/file/secondary.go b/middleware/file/secondary.go
deleted file mode 100644
index a37d62442..000000000
--- a/middleware/file/secondary.go
+++ /dev/null
@@ -1,199 +0,0 @@
-package file
-
-import (
- "log"
- "math/rand"
- "time"
-
- "github.com/miekg/dns"
-)
-
-// TransferIn retrieves the zone from the masters, parses it and sets it live.
-func (z *Zone) TransferIn() error {
- if len(z.TransferFrom) == 0 {
- return nil
- }
- m := new(dns.Msg)
- m.SetAxfr(z.origin)
-
- z1 := z.Copy()
- var (
- Err error
- tr string
- )
-
-Transfer:
- for _, tr = range z.TransferFrom {
- t := new(dns.Transfer)
- c, err := t.In(m, tr)
- if err != nil {
- log.Printf("[ERROR] Failed to setup transfer `%s' with `%q': %v", z.origin, tr, err)
- Err = err
- continue Transfer
- }
- for env := range c {
- if env.Error != nil {
- log.Printf("[ERROR] Failed to transfer `%s' from %q: %v", z.origin, tr, env.Error)
- Err = env.Error
- continue Transfer
- }
- for _, rr := range env.RR {
- if err := z1.Insert(rr); err != nil {
- log.Printf("[ERROR] Failed to parse transfer `%s' from: %q: %v", z.origin, tr, err)
- Err = err
- continue Transfer
- }
- }
- }
- Err = nil
- break
- }
- if Err != nil {
- return Err
- }
-
- z.Tree = z1.Tree
- z.Apex = z1.Apex
- *z.Expired = false
- log.Printf("[INFO] Transferred: %s from %s", z.origin, tr)
- return nil
-}
-
-// shouldTransfer checks the primaries of zone, retrieves the SOA record, checks the current serial
-// and the remote serial and will return true if the remote one is higher than the locally configured one.
-func (z *Zone) shouldTransfer() (bool, error) {
- c := new(dns.Client)
- c.Net = "tcp" // do this query over TCP to minimize spoofing
- m := new(dns.Msg)
- m.SetQuestion(z.origin, dns.TypeSOA)
-
- var Err error
- serial := -1
-
-Transfer:
- for _, tr := range z.TransferFrom {
- Err = nil
- ret, _, err := c.Exchange(m, tr)
- if err != nil || ret.Rcode != dns.RcodeSuccess {
- Err = err
- continue
- }
- for _, a := range ret.Answer {
- if a.Header().Rrtype == dns.TypeSOA {
- serial = int(a.(*dns.SOA).Serial)
- break Transfer
- }
- }
- }
- if serial == -1 {
- return false, Err
- }
- if z.Apex.SOA == nil {
- return true, Err
- }
- return less(z.Apex.SOA.Serial, uint32(serial)), Err
-}
-
-// less return true of a is smaller than b when taking RFC 1982 serial arithmetic into account.
-func less(a, b uint32) bool {
- if a < b {
- return (b - a) <= MaxSerialIncrement
- }
- return (a - b) > MaxSerialIncrement
-}
-
-// Update updates the secondary zone according to its SOA. It will run for the life time of the server
-// and uses the SOA parameters. Every refresh it will check for a new SOA number. If that fails (for all
-// server) it wil retry every retry interval. If the zone failed to transfer before the expire, the zone
-// will be marked expired.
-func (z *Zone) Update() error {
- // If we don't have a SOA, we don't have a zone, wait for it to appear.
- for z.Apex.SOA == nil {
- time.Sleep(1 * time.Second)
- }
- retryActive := false
-
-Restart:
- refresh := time.Second * time.Duration(z.Apex.SOA.Refresh)
- retry := time.Second * time.Duration(z.Apex.SOA.Retry)
- expire := time.Second * time.Duration(z.Apex.SOA.Expire)
-
- if refresh < time.Hour {
- refresh = time.Hour
- }
- if retry < time.Hour {
- retry = time.Hour
- }
- if refresh > 24*time.Hour {
- refresh = 24 * time.Hour
- }
- if retry > 12*time.Hour {
- retry = 12 * time.Hour
- }
-
- refreshTicker := time.NewTicker(refresh)
- retryTicker := time.NewTicker(retry)
- expireTicker := time.NewTicker(expire)
-
- for {
- select {
- case <-expireTicker.C:
- if !retryActive {
- break
- }
- *z.Expired = true
-
- case <-retryTicker.C:
- if !retryActive {
- break
- }
-
- time.Sleep(jitter(2000)) // 2s randomize
-
- ok, err := z.shouldTransfer()
- if err != nil && ok {
- if err := z.TransferIn(); err != nil {
- // transfer failed, leave retryActive true
- break
- }
- retryActive = false
- // transfer OK, possible new SOA, stop timers and redo
- refreshTicker.Stop()
- retryTicker.Stop()
- expireTicker.Stop()
- goto Restart
- }
-
- case <-refreshTicker.C:
-
- time.Sleep(jitter(5000)) // 5s randomize
-
- ok, err := z.shouldTransfer()
- retryActive = err != nil
- if err != nil && ok {
- if err := z.TransferIn(); err != nil {
- // transfer failed
- retryActive = true
- break
- }
- retryActive = false
- // transfer OK, possible new SOA, stop timers and redo
- refreshTicker.Stop()
- retryTicker.Stop()
- expireTicker.Stop()
- goto Restart
- }
- }
- }
-}
-
-// jitter returns a random duration between [0,n) * time.Millisecond
-func jitter(n int) time.Duration {
- r := rand.Intn(n)
- return time.Duration(r) * time.Millisecond
-
-}
-
-// MaxSerialIncrement is the maximum difference between two serial numbers. If the difference between
-// two serials is greater than this number, the smaller one is considered greater.
-const MaxSerialIncrement uint32 = 2147483647
diff --git a/middleware/file/secondary_test.go b/middleware/file/secondary_test.go
deleted file mode 100644
index cdb051d26..000000000
--- a/middleware/file/secondary_test.go
+++ /dev/null
@@ -1,168 +0,0 @@
-package file
-
-import (
- "fmt"
- "io/ioutil"
- "log"
- "testing"
-
- "github.com/coredns/coredns/middleware/test"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-// TODO(miek): should test notifies as well, ie start test server (a real coredns one)...
-// setup other test server that sends notify, see if CoreDNS comes calling for a zone
-// tranfer
-
-func TestLess(t *testing.T) {
- const (
- min = 0
- max = 4294967295
- low = 12345
- high = 4000000000
- )
-
- if less(min, max) {
- t.Fatalf("less: should be false")
- }
- if !less(max, min) {
- t.Fatalf("less: should be true")
- }
- if !less(high, low) {
- t.Fatalf("less: should be true")
- }
- if !less(7, 9) {
- t.Fatalf("less; should be true")
- }
-}
-
-type soa struct {
- serial uint32
-}
-
-func (s *soa) Handler(w dns.ResponseWriter, req *dns.Msg) {
- m := new(dns.Msg)
- m.SetReply(req)
- switch req.Question[0].Qtype {
- case dns.TypeSOA:
- m.Answer = make([]dns.RR, 1)
- m.Answer[0] = test.SOA(fmt.Sprintf("%s IN SOA bla. bla. %d 0 0 0 0 ", testZone, s.serial))
- w.WriteMsg(m)
- case dns.TypeAXFR:
- m.Answer = make([]dns.RR, 4)
- m.Answer[0] = test.SOA(fmt.Sprintf("%s IN SOA bla. bla. %d 0 0 0 0 ", testZone, s.serial))
- m.Answer[1] = test.A(fmt.Sprintf("%s IN A 127.0.0.1", testZone))
- m.Answer[2] = test.A(fmt.Sprintf("%s IN A 127.0.0.1", testZone))
- m.Answer[3] = test.SOA(fmt.Sprintf("%s IN SOA bla. bla. %d 0 0 0 0 ", testZone, s.serial))
- w.WriteMsg(m)
- }
-}
-
-func (s *soa) TransferHandler(w dns.ResponseWriter, req *dns.Msg) {
- m := new(dns.Msg)
- m.SetReply(req)
- m.Answer = make([]dns.RR, 1)
- m.Answer[0] = test.SOA(fmt.Sprintf("%s IN SOA bla. bla. %d 0 0 0 0 ", testZone, s.serial))
- w.WriteMsg(m)
-}
-
-const testZone = "secondary.miek.nl."
-
-func TestShouldTransfer(t *testing.T) {
- soa := soa{250}
- log.SetOutput(ioutil.Discard)
-
- dns.HandleFunc(testZone, soa.Handler)
- defer dns.HandleRemove(testZone)
-
- s, addrstr, err := test.TCPServer("127.0.0.1:0")
- if err != nil {
- t.Fatalf("unable to run test server: %v", err)
- }
- defer s.Shutdown()
-
- z := new(Zone)
- z.origin = testZone
- z.TransferFrom = []string{addrstr}
-
- // when we have a nil SOA (initial state)
- should, err := z.shouldTransfer()
- if err != nil {
- t.Fatalf("unable to run shouldTransfer: %v", err)
- }
- if !should {
- t.Fatalf("shouldTransfer should return true for serial: %d", soa.serial)
- }
- // Serial smaller
- z.Apex.SOA = test.SOA(fmt.Sprintf("%s IN SOA bla. bla. %d 0 0 0 0 ", testZone, soa.serial-1))
- should, err = z.shouldTransfer()
- if err != nil {
- t.Fatalf("unable to run shouldTransfer: %v", err)
- }
- if !should {
- t.Fatalf("shouldTransfer should return true for serial: %q", soa.serial-1)
- }
- // Serial equal
- z.Apex.SOA = test.SOA(fmt.Sprintf("%s IN SOA bla. bla. %d 0 0 0 0 ", testZone, soa.serial))
- should, err = z.shouldTransfer()
- if err != nil {
- t.Fatalf("unable to run shouldTransfer: %v", err)
- }
- if should {
- t.Fatalf("shouldTransfer should return false for serial: %d", soa.serial)
- }
-}
-
-func TestTransferIn(t *testing.T) {
- soa := soa{250}
- log.SetOutput(ioutil.Discard)
-
- dns.HandleFunc(testZone, soa.Handler)
- defer dns.HandleRemove(testZone)
-
- s, addrstr, err := test.TCPServer("127.0.0.1:0")
- if err != nil {
- t.Fatalf("unable to run test server: %v", err)
- }
- defer s.Shutdown()
-
- z := new(Zone)
- z.Expired = new(bool)
- z.origin = testZone
- z.TransferFrom = []string{addrstr}
-
- err = z.TransferIn()
- if err != nil {
- t.Fatalf("unable to run TransferIn: %v", err)
- }
- if z.Apex.SOA.String() != fmt.Sprintf("%s 3600 IN SOA bla. bla. 250 0 0 0 0", testZone) {
- t.Fatalf("unknown SOA transferred")
- }
-}
-
-func TestIsNotify(t *testing.T) {
- z := new(Zone)
- z.Expired = new(bool)
- z.origin = testZone
- state := newRequest(testZone, dns.TypeSOA)
- // need to set opcode
- state.Req.Opcode = dns.OpcodeNotify
-
- z.TransferFrom = []string{"10.240.0.1:53"} // IP from from testing/responseWriter
- if !z.isNotify(state) {
- t.Fatal("should have been valid notify")
- }
- z.TransferFrom = []string{"10.240.0.2:53"}
- if z.isNotify(state) {
- t.Fatal("should have been invalid notify")
- }
-}
-
-func newRequest(zone string, qtype uint16) request.Request {
- m := new(dns.Msg)
- m.SetQuestion("example.com.", dns.TypeA)
- m.SetEdns0(4097, true)
- return request.Request{W: &test.ResponseWriter{}, Req: m}
-}
diff --git a/middleware/file/setup.go b/middleware/file/setup.go
deleted file mode 100644
index 2ac7610c5..000000000
--- a/middleware/file/setup.go
+++ /dev/null
@@ -1,171 +0,0 @@
-package file
-
-import (
- "fmt"
- "os"
- "path"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/middleware/proxy"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("file", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- zones, err := fileParse(c)
- if err != nil {
- return middleware.Error("file", err)
- }
-
- // Add startup functions to notify the master(s).
- for _, n := range zones.Names {
- z := zones.Z[n]
- c.OnStartup(func() error {
- z.StartupOnce.Do(func() {
- if len(z.TransferTo) > 0 {
- z.Notify()
- }
- z.Reload()
- })
- return nil
- })
- }
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- return File{Next: next, Zones: zones}
- })
-
- return nil
-}
-
-func fileParse(c *caddy.Controller) (Zones, error) {
- z := make(map[string]*Zone)
- names := []string{}
- origins := []string{}
-
- config := dnsserver.GetConfig(c)
-
- for c.Next() {
- // file db.file [zones...]
- if !c.NextArg() {
- return Zones{}, c.ArgErr()
- }
- fileName := c.Val()
-
- origins = make([]string, len(c.ServerBlockKeys))
- copy(origins, c.ServerBlockKeys)
- args := c.RemainingArgs()
- if len(args) > 0 {
- origins = args
- }
-
- if !path.IsAbs(fileName) && config.Root != "" {
- fileName = path.Join(config.Root, fileName)
- }
-
- reader, err := os.Open(fileName)
- if err != nil {
- // bail out
- return Zones{}, err
- }
-
- for i := range origins {
- origins[i] = middleware.Host(origins[i]).Normalize()
- zone, err := Parse(reader, origins[i], fileName, 0)
- if err == nil {
- z[origins[i]] = zone
- } else {
- return Zones{}, err
- }
- names = append(names, origins[i])
- }
-
- noReload := false
- prxy := proxy.Proxy{}
- t := []string{}
- var e error
-
- for c.NextBlock() {
- switch c.Val() {
- case "transfer":
- t, _, e = TransferParse(c, false)
- if e != nil {
- return Zones{}, e
- }
-
- case "no_reload":
- noReload = true
-
- case "upstream":
- args := c.RemainingArgs()
- if len(args) == 0 {
- return Zones{}, c.ArgErr()
- }
- ups, err := dnsutil.ParseHostPortOrFile(args...)
- if err != nil {
- return Zones{}, err
- }
- prxy = proxy.NewLookup(ups)
- default:
- return Zones{}, c.Errf("unknown property '%s'", c.Val())
- }
-
- for _, origin := range origins {
- if t != nil {
- z[origin].TransferTo = append(z[origin].TransferTo, t...)
- }
- z[origin].NoReload = noReload
- z[origin].Proxy = prxy
- }
- }
- }
- return Zones{Z: z, Names: names}, nil
-}
-
-// TransferParse parses transfer statements: 'transfer to [address...]'.
-func TransferParse(c *caddy.Controller, secondary bool) (tos, froms []string, err error) {
- if !c.NextArg() {
- return nil, nil, c.ArgErr()
- }
- value := c.Val()
- switch value {
- case "to":
- tos = c.RemainingArgs()
- for i := range tos {
- if tos[i] != "*" {
- normalized, err := dnsutil.ParseHostPort(tos[i], "53")
- if err != nil {
- return nil, nil, err
- }
- tos[i] = normalized
- }
- }
-
- case "from":
- if !secondary {
- return nil, nil, fmt.Errorf("can't use `transfer from` when not being a secondary")
- }
- froms = c.RemainingArgs()
- for i := range froms {
- if froms[i] != "*" {
- normalized, err := dnsutil.ParseHostPort(froms[i], "53")
- if err != nil {
- return nil, nil, err
- }
- froms[i] = normalized
- } else {
- return nil, nil, fmt.Errorf("can't use '*' in transfer from")
- }
- }
- }
- return
-}
diff --git a/middleware/file/setup_test.go b/middleware/file/setup_test.go
deleted file mode 100644
index 111be1261..000000000
--- a/middleware/file/setup_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package file
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/mholt/caddy"
-)
-
-func TestFileParse(t *testing.T) {
- zoneFileName1, rm, err := test.TempFile(".", dbMiekNL)
- if err != nil {
- t.Fatal(err)
- }
- defer rm()
-
- zoneFileName2, rm, err := test.TempFile(".", dbDnssexNLSigned)
- if err != nil {
- t.Fatal(err)
- }
- defer rm()
-
- tests := []struct {
- inputFileRules string
- shouldErr bool
- expectedZones Zones
- }{
- {
- `file ` + zoneFileName1 + ` miek.nl {
- transfer from 127.0.0.1
- }`,
- true,
- Zones{},
- },
- {
- `file`,
- true,
- Zones{},
- },
- {
- `file ` + zoneFileName1 + ` miek.nl.`,
- false,
- Zones{Names: []string{"miek.nl."}},
- },
- {
- `file ` + zoneFileName2 + ` dnssex.nl.`,
- false,
- Zones{Names: []string{"dnssex.nl."}},
- },
- {
- `file ` + zoneFileName2 + ` 10.0.0.0/8`,
- false,
- Zones{Names: []string{"10.in-addr.arpa."}},
- },
- }
-
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.inputFileRules)
- actualZones, err := fileParse(c)
-
- if err == nil && test.shouldErr {
- t.Fatalf("Test %d expected errors, but got no error", i)
- } else if err != nil && !test.shouldErr {
- t.Fatalf("Test %d expected no errors, but got '%v'", i, err)
- } else {
- if len(actualZones.Names) != len(test.expectedZones.Names) {
- t.Fatalf("Test %d expected %v, got %v", i, test.expectedZones.Names, actualZones.Names)
- }
- for j, name := range test.expectedZones.Names {
- if actualZones.Names[j] != name {
- t.Fatalf("Test %d expected %v for %d th zone, got %v", i, name, j, actualZones.Names[j])
- }
- }
- }
- }
-}
diff --git a/middleware/file/tree/all.go b/middleware/file/tree/all.go
deleted file mode 100644
index fd806365f..000000000
--- a/middleware/file/tree/all.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package tree
-
-// All traverses tree and returns all elements
-func (t *Tree) All() []*Elem {
- if t.Root == nil {
- return nil
- }
- found := t.Root.all(nil)
- return found
-}
-
-func (n *Node) all(found []*Elem) []*Elem {
- if n.Left != nil {
- found = n.Left.all(found)
- }
- found = append(found, n.Elem)
- if n.Right != nil {
- found = n.Right.all(found)
- }
- return found
-}
-
-// Do performs fn on all values stored in the tree. A boolean is returned indicating whether the
-// Do traversal was interrupted by an Operation returning true. If fn alters stored values' sort
-// relationships, future tree operation behaviors are undefined.
-func (t *Tree) Do(fn func(e *Elem) bool) bool {
- if t.Root == nil {
- return false
- }
- return t.Root.do(fn)
-}
-
-func (n *Node) do(fn func(e *Elem) bool) (done bool) {
- if n.Left != nil {
- done = n.Left.do(fn)
- if done {
- return
- }
- }
- done = fn(n.Elem)
- if done {
- return
- }
- if n.Right != nil {
- done = n.Right.do(fn)
- }
- return
-}
diff --git a/middleware/file/tree/elem.go b/middleware/file/tree/elem.go
deleted file mode 100644
index 6317cc912..000000000
--- a/middleware/file/tree/elem.go
+++ /dev/null
@@ -1,136 +0,0 @@
-package tree
-
-import "github.com/miekg/dns"
-
-// Elem is an element in the tree.
-type Elem struct {
- m map[uint16][]dns.RR
- name string // owner name
-}
-
-// newElem returns a new elem.
-func newElem(rr dns.RR) *Elem {
- e := Elem{m: make(map[uint16][]dns.RR)}
- e.m[rr.Header().Rrtype] = []dns.RR{rr}
- return &e
-}
-
-// Types returns the RRs with type qtype from e. If qname is given (only the
-// first one is used), the RR are copied and the owner is replaced with qname[0].
-func (e *Elem) Types(qtype uint16, qname ...string) []dns.RR {
- rrs := e.m[qtype]
-
- if rrs != nil && len(qname) > 0 {
- copied := make([]dns.RR, len(rrs))
- for i := range rrs {
- copied[i] = dns.Copy(rrs[i])
- copied[i].Header().Name = qname[0]
- }
- return copied
- }
- return rrs
-}
-
-// All returns all RRs from e, regardless of type.
-func (e *Elem) All() []dns.RR {
- list := []dns.RR{}
- for _, rrs := range e.m {
- list = append(list, rrs...)
- }
- return list
-}
-
-// Name returns the name for this node.
-func (e *Elem) Name() string {
- if e.name != "" {
- return e.name
- }
- for _, rrs := range e.m {
- e.name = rrs[0].Header().Name
- return e.name
- }
- return ""
-}
-
-// Empty returns true is e does not contain any RRs, i.e. is an
-// empty-non-terminal.
-func (e *Elem) Empty() bool {
- return len(e.m) == 0
-}
-
-// Insert inserts rr into e. If rr is equal to existing rrs this is a noop.
-func (e *Elem) Insert(rr dns.RR) {
- t := rr.Header().Rrtype
- if e.m == nil {
- e.m = make(map[uint16][]dns.RR)
- e.m[t] = []dns.RR{rr}
- return
- }
- rrs, ok := e.m[t]
- if !ok {
- e.m[t] = []dns.RR{rr}
- return
- }
- for _, er := range rrs {
- if equalRdata(er, rr) {
- return
- }
- }
-
- rrs = append(rrs, rr)
- e.m[t] = rrs
-}
-
-// Delete removes rr from e. When e is empty after the removal the returned bool is true.
-func (e *Elem) Delete(rr dns.RR) (empty bool) {
- if e.m == nil {
- return true
- }
-
- t := rr.Header().Rrtype
- rrs, ok := e.m[t]
- if !ok {
- return
- }
-
- for i, er := range rrs {
- if equalRdata(er, rr) {
- rrs = removeFromSlice(rrs, i)
- e.m[t] = rrs
- empty = len(rrs) == 0
- if empty {
- delete(e.m, t)
- }
- return
- }
- }
- return
-}
-
-// Less is a tree helper function that calls less.
-func Less(a *Elem, name string) int { return less(name, a.Name()) }
-
-// Assuming the same type and name this will check if the rdata is equal as well.
-func equalRdata(a, b dns.RR) bool {
- switch x := a.(type) {
- // TODO(miek): more types, i.e. all types. + tests for this.
- case *dns.A:
- return x.A.Equal(b.(*dns.A).A)
- case *dns.AAAA:
- return x.AAAA.Equal(b.(*dns.AAAA).AAAA)
- case *dns.MX:
- if x.Mx == b.(*dns.MX).Mx && x.Preference == b.(*dns.MX).Preference {
- return true
- }
- }
- return false
-}
-
-// removeFromSlice removes index i from the slice.
-func removeFromSlice(rrs []dns.RR, i int) []dns.RR {
- if i >= len(rrs) {
- return rrs
- }
- rrs = append(rrs[:i], rrs[i+1:]...)
- return rrs
-}
diff --git a/middleware/file/tree/less.go b/middleware/file/tree/less.go
deleted file mode 100644
index 3b8340088..000000000
--- a/middleware/file/tree/less.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package tree
-
-import (
- "bytes"
-
- "github.com/miekg/dns"
-)
-
-// less returns <0 when a is less than b, 0 when they are equal and
-// >0 when a is larger than b.
-// The function orders names in DNSSEC canonical order: RFC 4034s section-6.1
-//
-// See http://bert-hubert.blogspot.co.uk/2015/10/how-to-do-fast-canonical-ordering-of.html
-// for a blog article on this implementation, although here we still go label by label.
-//
-// The values of a and b are *not* lowercased before the comparison!
-func less(a, b string) int {
- i := 1
- aj := len(a)
- bj := len(b)
- for {
- ai, oka := dns.PrevLabel(a, i)
- bi, okb := dns.PrevLabel(b, i)
- if oka && okb {
- return 0
- }
-
- // sadly this []byte will allocate... TODO(miek): check if this is needed
- // for a name, otherwise compare the strings.
- ab := []byte(a[ai:aj])
- bb := []byte(b[bi:bj])
- doDDD(ab)
- doDDD(bb)
-
- res := bytes.Compare(ab, bb)
- if res != 0 {
- return res
- }
-
- i++
- aj, bj = ai, bi
- }
-}
-
-func doDDD(b []byte) {
- lb := len(b)
- for i := 0; i < lb; i++ {
- if i+3 < lb && b[i] == '\\' && isDigit(b[i+1]) && isDigit(b[i+2]) && isDigit(b[i+3]) {
- b[i] = dddToByte(b[i:])
- for j := i + 1; j < lb-3; j++ {
- b[j] = b[j+3]
- }
- lb -= 3
- }
- }
-}
-
-func isDigit(b byte) bool { return b >= '0' && b <= '9' }
-func dddToByte(s []byte) byte { return (s[1]-'0')*100 + (s[2]-'0')*10 + (s[3] - '0') }
diff --git a/middleware/file/tree/less_test.go b/middleware/file/tree/less_test.go
deleted file mode 100644
index ed021b66f..000000000
--- a/middleware/file/tree/less_test.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package tree
-
-import (
- "sort"
- "strings"
- "testing"
-)
-
-type set []string
-
-func (p set) Len() int { return len(p) }
-func (p set) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
-func (p set) Less(i, j int) bool { d := less(p[i], p[j]); return d <= 0 }
-
-func TestLess(t *testing.T) {
- tests := []struct {
- in []string
- out []string
- }{
- {
- []string{"aaa.powerdns.de", "bbb.powerdns.net.", "xxx.powerdns.com."},
- []string{"xxx.powerdns.com.", "aaa.powerdns.de", "bbb.powerdns.net."},
- },
- {
- []string{"aaa.POWERDNS.de", "bbb.PoweRdnS.net.", "xxx.powerdns.com."},
- []string{"xxx.powerdns.com.", "aaa.POWERDNS.de", "bbb.PoweRdnS.net."},
- },
- {
- []string{"aaa.aaaa.aa.", "aa.aaa.a.", "bbb.bbbb.bb."},
- []string{"aa.aaa.a.", "aaa.aaaa.aa.", "bbb.bbbb.bb."},
- },
- {
- []string{"aaaaa.", "aaa.", "bbb."},
- []string{"aaa.", "aaaaa.", "bbb."},
- },
- {
- []string{"a.a.a.a.", "a.a.", "a.a.a."},
- []string{"a.a.", "a.a.a.", "a.a.a.a."},
- },
- {
- []string{"example.", "z.example.", "a.example."},
- []string{"example.", "a.example.", "z.example."},
- },
- {
- []string{"a.example.", "Z.a.example.", "z.example.", "yljkjljk.a.example.", "\\001.z.example.", "example.", "*.z.example.", "\\200.z.example.", "zABC.a.EXAMPLE."},
- []string{"example.", "a.example.", "yljkjljk.a.example.", "Z.a.example.", "zABC.a.EXAMPLE.", "z.example.", "\\001.z.example.", "*.z.example.", "\\200.z.example."},
- },
- {
- // RFC3034 example.
- []string{"a.example.", "Z.a.example.", "z.example.", "yljkjljk.a.example.", "example.", "*.z.example.", "zABC.a.EXAMPLE."},
- []string{"example.", "a.example.", "yljkjljk.a.example.", "Z.a.example.", "zABC.a.EXAMPLE.", "z.example.", "*.z.example."},
- },
- }
-
-Tests:
- for j, test := range tests {
- // Need to lowercase these example as the Less function does lowercase for us anymore.
- for i, b := range test.in {
- test.in[i] = strings.ToLower(b)
- }
- for i, b := range test.out {
- test.out[i] = strings.ToLower(b)
- }
-
- sort.Sort(set(test.in))
- for i := 0; i < len(test.in); i++ {
- if test.in[i] != test.out[i] {
- t.Errorf("Test %d: expected %s, got %s\n", j, test.out[i], test.in[i])
- n := ""
- for k, in := range test.in {
- if k+1 == len(test.in) {
- n = "\n"
- }
- t.Logf("%s <-> %s\n%s", in, test.out[k], n)
- }
- continue Tests
- }
-
- }
- }
-}
diff --git a/middleware/file/tree/print.go b/middleware/file/tree/print.go
deleted file mode 100644
index bd86ef690..000000000
--- a/middleware/file/tree/print.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package tree
-
-import "fmt"
-
-// Print prints a Tree. Main use is to aid in debugging.
-func (t *Tree) Print() {
- if t.Root == nil {
- fmt.Println("<nil>")
- }
- t.Root.print()
-}
-
-func (n *Node) print() {
- q := newQueue()
- q.push(n)
-
- nodesInCurrentLevel := 1
- nodesInNextLevel := 0
-
- for !q.empty() {
- do := q.pop()
- nodesInCurrentLevel--
-
- if do != nil {
- fmt.Print(do.Elem.Name(), " ")
- q.push(do.Left)
- q.push(do.Right)
- nodesInNextLevel += 2
- }
- if nodesInCurrentLevel == 0 {
- fmt.Println()
- }
- nodesInCurrentLevel = nodesInNextLevel
- nodesInNextLevel = 0
- }
- fmt.Println()
-}
-
-type queue []*Node
-
-// newQueue returns a new queue.
-func newQueue() queue {
- q := queue([]*Node{})
- return q
-}
-
-// push pushes n to the end of the queue.
-func (q *queue) push(n *Node) {
- *q = append(*q, n)
-}
-
-// pop pops the first element off the queue.
-func (q *queue) pop() *Node {
- n := (*q)[0]
- *q = (*q)[1:]
- return n
-}
-
-// empty returns true when the queue contains zero nodes.
-func (q *queue) empty() bool {
- return len(*q) == 0
-}
diff --git a/middleware/file/tree/tree.go b/middleware/file/tree/tree.go
deleted file mode 100644
index ed33c09a4..000000000
--- a/middleware/file/tree/tree.go
+++ /dev/null
@@ -1,455 +0,0 @@
-// Copyright ©2012 The bíogo Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found at the end of this file.
-
-// Package tree implements Left-Leaning Red Black trees as described by Robert Sedgewick.
-//
-// More details relating to the implementation are available at the following locations:
-//
-// http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf
-// http://www.cs.princeton.edu/~rs/talks/LLRB/Java/RedBlackBST.java
-// http://www.teachsolaisgames.com/articles/balanced_left_leaning.html
-//
-// Heavily modified by Miek Gieben for use in DNS zones.
-package tree
-
-import "github.com/miekg/dns"
-
-const (
- td234 = iota
- bu23
-)
-
-// Operation mode of the LLRB tree.
-const mode = bu23
-
-func init() {
- if mode != td234 && mode != bu23 {
- panic("tree: unknown mode")
- }
-}
-
-// A Color represents the color of a Node.
-type Color bool
-
-const (
- // Red as false give us the defined behaviour that new nodes are red. Although this
- // is incorrect for the root node, that is resolved on the first insertion.
- red Color = false
- black Color = true
-)
-
-// A Node represents a node in the LLRB tree.
-type Node struct {
- Elem *Elem
- Left, Right *Node
- Color Color
-}
-
-// A Tree manages the root node of an LLRB tree. Public methods are exposed through this type.
-type Tree struct {
- Root *Node // Root node of the tree.
- Count int // Number of elements stored.
-}
-
-// Helper methods
-
-// color returns the effect color of a Node. A nil node returns black.
-func (n *Node) color() Color {
- if n == nil {
- return black
- }
- return n.Color
-}
-
-// (a,c)b -rotL-> ((a,)b,)c
-func (n *Node) rotateLeft() (root *Node) {
- // Assumes: n has two children.
- root = n.Right
- n.Right = root.Left
- root.Left = n
- root.Color = n.Color
- n.Color = red
- return
-}
-
-// (a,c)b -rotR-> (,(,c)b)a
-func (n *Node) rotateRight() (root *Node) {
- // Assumes: n has two children.
- root = n.Left
- n.Left = root.Right
- root.Right = n
- root.Color = n.Color
- n.Color = red
- return
-}
-
-// (aR,cR)bB -flipC-> (aB,cB)bR | (aB,cB)bR -flipC-> (aR,cR)bB
-func (n *Node) flipColors() {
- // Assumes: n has two children.
- n.Color = !n.Color
- n.Left.Color = !n.Left.Color
- n.Right.Color = !n.Right.Color
-}
-
-// fixUp ensures that black link balance is correct, that red nodes lean left,
-// and that 4 nodes are split in the case of BU23 and properly balanced in TD234.
-func (n *Node) fixUp() *Node {
- if n.Right.color() == red {
- if mode == td234 && n.Right.Left.color() == red {
- n.Right = n.Right.rotateRight()
- }
- n = n.rotateLeft()
- }
- if n.Left.color() == red && n.Left.Left.color() == red {
- n = n.rotateRight()
- }
- if mode == bu23 && n.Left.color() == red && n.Right.color() == red {
- n.flipColors()
- }
- return n
-}
-
-func (n *Node) moveRedLeft() *Node {
- n.flipColors()
- if n.Right.Left.color() == red {
- n.Right = n.Right.rotateRight()
- n = n.rotateLeft()
- n.flipColors()
- if mode == td234 && n.Right.Right.color() == red {
- n.Right = n.Right.rotateLeft()
- }
- }
- return n
-}
-
-func (n *Node) moveRedRight() *Node {
- n.flipColors()
- if n.Left.Left.color() == red {
- n = n.rotateRight()
- n.flipColors()
- }
- return n
-}
-
-// Len returns the number of elements stored in the Tree.
-func (t *Tree) Len() int {
- return t.Count
-}
-
-// Search returns the first match of qname in the Tree.
-func (t *Tree) Search(qname string) (*Elem, bool) {
- if t.Root == nil {
- return nil, false
- }
- n, res := t.Root.search(qname)
- if n == nil {
- return nil, res
- }
- return n.Elem, res
-}
-
-// search searches the tree for qname and type.
-func (n *Node) search(qname string) (*Node, bool) {
- for n != nil {
- switch c := Less(n.Elem, qname); {
- case c == 0:
- return n, true
- case c < 0:
- n = n.Left
- default:
- n = n.Right
- }
- }
-
- return n, false
-}
-
-// Insert inserts rr into the Tree at the first match found
-// with e or when a nil node is reached.
-func (t *Tree) Insert(rr dns.RR) {
- var d int
- t.Root, d = t.Root.insert(rr)
- t.Count += d
- t.Root.Color = black
-}
-
-// insert inserts rr in to the tree.
-func (n *Node) insert(rr dns.RR) (root *Node, d int) {
- if n == nil {
- return &Node{Elem: newElem(rr)}, 1
- } else if n.Elem == nil {
- n.Elem = newElem(rr)
- return n, 1
- }
-
- if mode == td234 {
- if n.Left.color() == red && n.Right.color() == red {
- n.flipColors()
- }
- }
-
- switch c := Less(n.Elem, rr.Header().Name); {
- case c == 0:
- n.Elem.Insert(rr)
- case c < 0:
- n.Left, d = n.Left.insert(rr)
- default:
- n.Right, d = n.Right.insert(rr)
- }
-
- if n.Right.color() == red && n.Left.color() == black {
- n = n.rotateLeft()
- }
- if n.Left.color() == red && n.Left.Left.color() == red {
- n = n.rotateRight()
- }
-
- if mode == bu23 {
- if n.Left.color() == red && n.Right.color() == red {
- n.flipColors()
- }
- }
-
- root = n
-
- return
-}
-
-// DeleteMin deletes the node with the minimum value in the tree.
-func (t *Tree) DeleteMin() {
- if t.Root == nil {
- return
- }
- var d int
- t.Root, d = t.Root.deleteMin()
- t.Count += d
- if t.Root == nil {
- return
- }
- t.Root.Color = black
-}
-
-func (n *Node) deleteMin() (root *Node, d int) {
- if n.Left == nil {
- return nil, -1
- }
- if n.Left.color() == black && n.Left.Left.color() == black {
- n = n.moveRedLeft()
- }
- n.Left, d = n.Left.deleteMin()
-
- root = n.fixUp()
-
- return
-}
-
-// DeleteMax deletes the node with the maximum value in the tree.
-func (t *Tree) DeleteMax() {
- if t.Root == nil {
- return
- }
- var d int
- t.Root, d = t.Root.deleteMax()
- t.Count += d
- if t.Root == nil {
- return
- }
- t.Root.Color = black
-}
-
-func (n *Node) deleteMax() (root *Node, d int) {
- if n.Left != nil && n.Left.color() == red {
- n = n.rotateRight()
- }
- if n.Right == nil {
- return nil, -1
- }
- if n.Right.color() == black && n.Right.Left.color() == black {
- n = n.moveRedRight()
- }
- n.Right, d = n.Right.deleteMax()
-
- root = n.fixUp()
-
- return
-}
-
-// Delete removes rr from the tree, is the node turns empty, that node is deleted with DeleteNode.
-func (t *Tree) Delete(rr dns.RR) {
- if t.Root == nil {
- return
- }
-
- el, _ := t.Search(rr.Header().Name)
- if el == nil {
- t.deleteNode(rr)
- return
- }
- // Delete from this element.
- empty := el.Delete(rr)
- if empty {
- t.deleteNode(rr)
- return
- }
-}
-
-// DeleteNode deletes the node that matches rr according to Less().
-func (t *Tree) deleteNode(rr dns.RR) {
- if t.Root == nil {
- return
- }
- var d int
- t.Root, d = t.Root.delete(rr)
- t.Count += d
- if t.Root == nil {
- return
- }
- t.Root.Color = black
-}
-
-func (n *Node) delete(rr dns.RR) (root *Node, d int) {
- if Less(n.Elem, rr.Header().Name) < 0 {
- if n.Left != nil {
- if n.Left.color() == black && n.Left.Left.color() == black {
- n = n.moveRedLeft()
- }
- n.Left, d = n.Left.delete(rr)
- }
- } else {
- if n.Left.color() == red {
- n = n.rotateRight()
- }
- if n.Right == nil && Less(n.Elem, rr.Header().Name) == 0 {
- return nil, -1
- }
- if n.Right != nil {
- if n.Right.color() == black && n.Right.Left.color() == black {
- n = n.moveRedRight()
- }
- if Less(n.Elem, rr.Header().Name) == 0 {
- n.Elem = n.Right.min().Elem
- n.Right, d = n.Right.deleteMin()
- } else {
- n.Right, d = n.Right.delete(rr)
- }
- }
- }
-
- root = n.fixUp()
- return
-}
-
-// Min returns the minimum value stored in the tree.
-func (t *Tree) Min() *Elem {
- if t.Root == nil {
- return nil
- }
- return t.Root.min().Elem
-}
-
-func (n *Node) min() *Node {
- for ; n.Left != nil; n = n.Left {
- }
- return n
-}
-
-// Max returns the maximum value stored in the tree.
-func (t *Tree) Max() *Elem {
- if t.Root == nil {
- return nil
- }
- return t.Root.max().Elem
-}
-
-func (n *Node) max() *Node {
- for ; n.Right != nil; n = n.Right {
- }
- return n
-}
-
-// Prev returns the greatest value equal to or less than the qname according to Less().
-func (t *Tree) Prev(qname string) (*Elem, bool) {
- if t.Root == nil {
- return nil, false
- }
-
- n := t.Root.floor(qname)
- if n == nil {
- return nil, false
- }
- return n.Elem, true
-}
-
-func (n *Node) floor(qname string) *Node {
- if n == nil {
- return nil
- }
- switch c := Less(n.Elem, qname); {
- case c == 0:
- return n
- case c <= 0:
- return n.Left.floor(qname)
- default:
- if r := n.Right.floor(qname); r != nil {
- return r
- }
- }
- return n
-}
-
-// Next returns the smallest value equal to or greater than the qname according to Less().
-func (t *Tree) Next(qname string) (*Elem, bool) {
- if t.Root == nil {
- return nil, false
- }
- n := t.Root.ceil(qname)
- if n == nil {
- return nil, false
- }
- return n.Elem, true
-}
-
-func (n *Node) ceil(qname string) *Node {
- if n == nil {
- return nil
- }
- switch c := Less(n.Elem, qname); {
- case c == 0:
- return n
- case c > 0:
- return n.Right.ceil(qname)
- default:
- if l := n.Left.ceil(qname); l != nil {
- return l
- }
- }
- return n
-}
-
-/*
-Copyright ©2012 The bíogo Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-* Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-* Neither the name of the bíogo project nor the names of its authors and
- contributors may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
diff --git a/middleware/file/wildcard.go b/middleware/file/wildcard.go
deleted file mode 100644
index 9526cb53f..000000000
--- a/middleware/file/wildcard.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package file
-
-import "github.com/miekg/dns"
-
-// replaceWithWildcard replaces the left most label with '*'.
-func replaceWithAsteriskLabel(qname string) (wildcard string) {
- i, shot := dns.NextLabel(qname, 0)
- if shot {
- return ""
- }
-
- return "*." + qname[i:]
-}
diff --git a/middleware/file/wildcard_test.go b/middleware/file/wildcard_test.go
deleted file mode 100644
index 7f1a2fed3..000000000
--- a/middleware/file/wildcard_test.go
+++ /dev/null
@@ -1,289 +0,0 @@
-package file
-
-import (
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-var wildcardTestCases = []test.Case{
- {
- Qname: "wild.dnssex.nl.", Qtype: dns.TypeTXT,
- Answer: []dns.RR{
- test.TXT(`wild.dnssex.nl. 1800 IN TXT "Doing It Safe Is Better"`),
- },
- Ns: dnssexAuth[:len(dnssexAuth)-1], // remove RRSIG on the end
- },
- {
- Qname: "a.wild.dnssex.nl.", Qtype: dns.TypeTXT,
- Answer: []dns.RR{
- test.TXT(`a.wild.dnssex.nl. 1800 IN TXT "Doing It Safe Is Better"`),
- },
- Ns: dnssexAuth[:len(dnssexAuth)-1], // remove RRSIG on the end
- },
- {
- Qname: "wild.dnssex.nl.", Qtype: dns.TypeTXT, Do: true,
- Answer: []dns.RR{
- test.RRSIG("wild.dnssex.nl. 1800 IN RRSIG TXT 8 2 1800 20160428190224 20160329190224 14460 dnssex.nl. FUZSTyvZfeuuOpCm"),
- test.TXT(`wild.dnssex.nl. 1800 IN TXT "Doing It Safe Is Better"`),
- },
- Ns: append([]dns.RR{
- test.NSEC("a.dnssex.nl. 14400 IN NSEC www.dnssex.nl. A AAAA RRSIG NSEC"),
- test.RRSIG("a.dnssex.nl. 14400 IN RRSIG NSEC 8 3 14400 20160428190224 20160329190224 14460 dnssex.nl. S+UMs2ySgRaaRY"),
- }, dnssexAuth...),
- Extra: []dns.RR{test.OPT(4096, true)},
- },
- {
- Qname: "a.wild.dnssex.nl.", Qtype: dns.TypeTXT, Do: true,
- Answer: []dns.RR{
- test.RRSIG("a.wild.dnssex.nl. 1800 IN RRSIG TXT 8 2 1800 20160428190224 20160329190224 14460 dnssex.nl. FUZSTyvZfeuuOpCm"),
- test.TXT(`a.wild.dnssex.nl. 1800 IN TXT "Doing It Safe Is Better"`),
- },
- Ns: append([]dns.RR{
- test.NSEC("a.dnssex.nl. 14400 IN NSEC www.dnssex.nl. A AAAA RRSIG NSEC"),
- test.RRSIG("a.dnssex.nl. 14400 IN RRSIG NSEC 8 3 14400 20160428190224 20160329190224 14460 dnssex.nl. S+UMs2ySgRaaRY"),
- }, dnssexAuth...),
- Extra: []dns.RR{test.OPT(4096, true)},
- },
- // nodata responses
- {
- Qname: "wild.dnssex.nl.", Qtype: dns.TypeSRV,
- Ns: []dns.RR{
- test.SOA(`dnssex.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1459281744 14400 3600 604800 14400`),
- },
- },
- {
- Qname: "wild.dnssex.nl.", Qtype: dns.TypeSRV, Do: true,
- Ns: []dns.RR{
- // TODO(miek): needs closest encloser proof as well? This is the wrong answer
- test.NSEC(`*.dnssex.nl. 14400 IN NSEC a.dnssex.nl. TXT RRSIG NSEC`),
- test.RRSIG(`*.dnssex.nl. 14400 IN RRSIG NSEC 8 2 14400 20160428190224 20160329190224 14460 dnssex.nl. os6INm6q2eXknD5z8TaaDOV+Ge/Ko+2dXnKP+J1fqJzafXJVH1F0nDrcXmMlR6jlBHA=`),
- test.RRSIG(`dnssex.nl. 1800 IN RRSIG SOA 8 2 1800 20160428190224 20160329190224 14460 dnssex.nl. CA/Y3m9hCOiKC/8ieSOv8SeP964Bq++lyH8BZJcTaabAsERs4xj5PRtcxicwQXZiF8fYUCpROlUS0YR8Cdw=`),
- test.SOA(`dnssex.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1459281744 14400 3600 604800 14400`),
- },
- Extra: []dns.RR{test.OPT(4096, true)},
- },
-}
-
-var dnssexAuth = []dns.RR{
- test.NS("dnssex.nl. 1800 IN NS linode.atoom.net."),
- test.NS("dnssex.nl. 1800 IN NS ns-ext.nlnetlabs.nl."),
- test.NS("dnssex.nl. 1800 IN NS omval.tednet.nl."),
- test.RRSIG("dnssex.nl. 1800 IN RRSIG NS 8 2 1800 20160428190224 20160329190224 14460 dnssex.nl. dLIeEvP86jj5ndkcLzhgvWixTABjWAGRTGQsPsVDFXsGMf9TGGC9FEomgkCVeNC0="),
-}
-
-func TestLookupWildcard(t *testing.T) {
- zone, err := Parse(strings.NewReader(dbDnssexNLSigned), testzone1, "stdin", 0)
- if err != nil {
- t.Fatalf("Expect no error when reading zone, got %q", err)
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{testzone1: zone}, Names: []string{testzone1}}}
- ctx := context.TODO()
-
- for _, tc := range wildcardTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-var wildcardDoubleTestCases = []test.Case{
- {
- Qname: "wild.w.example.org.", Qtype: dns.TypeTXT,
- Answer: []dns.RR{
- test.TXT(`wild.w.example.org. IN TXT "Wildcard"`),
- },
- Ns: exampleAuth,
- },
- {
- Qname: "wild.c.example.org.", Qtype: dns.TypeTXT,
- Answer: []dns.RR{
- test.TXT(`wild.c.example.org. IN TXT "c Wildcard"`),
- },
- Ns: exampleAuth,
- },
- {
- Qname: "wild.d.example.org.", Qtype: dns.TypeTXT,
- Answer: []dns.RR{
- test.TXT(`alias.example.org. IN TXT "Wildcard CNAME expansion"`),
- test.CNAME(`wild.d.example.org. IN CNAME alias.example.org`),
- },
- Ns: exampleAuth,
- },
- {
- Qname: "alias.example.org.", Qtype: dns.TypeTXT,
- Answer: []dns.RR{
- test.TXT(`alias.example.org. IN TXT "Wildcard CNAME expansion"`),
- },
- Ns: exampleAuth,
- },
-}
-
-var exampleAuth = []dns.RR{
- test.NS("example.org. 3600 IN NS a.iana-servers.net."),
- test.NS("example.org. 3600 IN NS b.iana-servers.net."),
-}
-
-func TestLookupDoubleWildcard(t *testing.T) {
- zone, err := Parse(strings.NewReader(exampleOrg), "example.org.", "stdin", 0)
- if err != nil {
- t.Fatalf("Expect no error when reading zone, got %q", err)
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{"example.org.": zone}, Names: []string{"example.org."}}}
- ctx := context.TODO()
-
- for _, tc := range wildcardDoubleTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-func TestReplaceWithAsteriskLabel(t *testing.T) {
- tests := []struct {
- in, out string
- }{
- {".", ""},
- {"miek.nl.", "*.nl."},
- {"www.miek.nl.", "*.miek.nl."},
- }
-
- for _, tc := range tests {
- got := replaceWithAsteriskLabel(tc.in)
- if got != tc.out {
- t.Errorf("Expected to be %s, got %s", tc.out, got)
- }
- }
-}
-
-var apexWildcardTestCases = []test.Case{
- {
- Qname: "foo.example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{test.A(`foo.example.org. 3600 IN A 127.0.0.54`)},
- Ns: []dns.RR{test.NS(`example.org. 3600 IN NS b.iana-servers.net.`)},
- },
- {
- Qname: "bar.example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{test.A(`bar.example.org. 3600 IN A 127.0.0.53`)},
- Ns: []dns.RR{test.NS(`example.org. 3600 IN NS b.iana-servers.net.`)},
- },
-}
-
-func TestLookupApexWildcard(t *testing.T) {
- const name = "example.org."
- zone, err := Parse(strings.NewReader(apexWildcard), name, "stdin", 0)
- if err != nil {
- t.Fatalf("Expect no error when reading zone, got %q", err)
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{name: zone}, Names: []string{name}}}
- ctx := context.TODO()
-
- for _, tc := range apexWildcardTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-var multiWildcardTestCases = []test.Case{
- {
- Qname: "foo.example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{test.A(`foo.example.org. 3600 IN A 127.0.0.54`)},
- Ns: []dns.RR{test.NS(`example.org. 3600 IN NS b.iana-servers.net.`)},
- },
- {
- Qname: "bar.example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{test.A(`bar.example.org. 3600 IN A 127.0.0.53`)},
- Ns: []dns.RR{test.NS(`example.org. 3600 IN NS b.iana-servers.net.`)},
- },
- {
- Qname: "bar.intern.example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{test.A(`bar.intern.example.org. 3600 IN A 127.0.1.52`)},
- Ns: []dns.RR{test.NS(`example.org. 3600 IN NS b.iana-servers.net.`)},
- },
-}
-
-func TestLookupMultiWildcard(t *testing.T) {
- const name = "example.org."
- zone, err := Parse(strings.NewReader(doubleWildcard), name, "stdin", 0)
- if err != nil {
- t.Fatalf("Expect no error when reading zone, got %q", err)
- }
-
- fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{name: zone}, Names: []string{name}}}
- ctx := context.TODO()
-
- for _, tc := range multiWildcardTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := fm.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-const exampleOrg = `; example.org test file
-example.org. IN SOA sns.dns.icann.org. noc.dns.icann.org. 2015082541 7200 3600 1209600 3600
-example.org. IN NS b.iana-servers.net.
-example.org. IN NS a.iana-servers.net.
-example.org. IN A 127.0.0.1
-example.org. IN A 127.0.0.2
-*.w.example.org. IN TXT "Wildcard"
-a.b.c.w.example.org. IN TXT "Not a wildcard"
-*.c.example.org. IN TXT "c Wildcard"
-*.d.example.org. IN CNAME alias.example.org.
-alias.example.org. IN TXT "Wildcard CNAME expansion"
-`
-
-const apexWildcard = `; example.org test file with wildcard at apex
-example.org. IN SOA sns.dns.icann.org. noc.dns.icann.org. 2015082541 7200 3600 1209600 3600
-example.org. IN NS b.iana-servers.net.
-*.example.org. IN A 127.0.0.53
-foo.example.org. IN A 127.0.0.54
-`
-
-const doubleWildcard = `; example.org test file with wildcard at apex
-example.org. IN SOA sns.dns.icann.org. noc.dns.icann.org. 2015082541 7200 3600 1209600 3600
-example.org. IN NS b.iana-servers.net.
-*.example.org. IN A 127.0.0.53
-*.intern.example.org. IN A 127.0.1.52
-foo.example.org. IN A 127.0.0.54
-`
diff --git a/middleware/file/xfr.go b/middleware/file/xfr.go
deleted file mode 100644
index 54f7b71f8..000000000
--- a/middleware/file/xfr.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package file
-
-import (
- "fmt"
- "log"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// Xfr serves up an AXFR.
-type Xfr struct {
- *Zone
-}
-
-// ServeDNS implements the middleware.Handler interface.
-func (x Xfr) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- state := request.Request{W: w, Req: r}
- if !x.TransferAllowed(state) {
- return dns.RcodeServerFailure, nil
- }
- if state.QType() != dns.TypeAXFR && state.QType() != dns.TypeIXFR {
- return 0, middleware.Error(x.Name(), fmt.Errorf("xfr called with non transfer type: %d", state.QType()))
- }
-
- records := x.All()
- if len(records) == 0 {
- return dns.RcodeServerFailure, nil
- }
-
- ch := make(chan *dns.Envelope)
- defer close(ch)
- tr := new(dns.Transfer)
- go tr.Out(w, r, ch)
-
- j, l := 0, 0
- records = append(records, records[0]) // add closing SOA to the end
- log.Printf("[INFO] Outgoing transfer of %d records of zone %s to %s started", len(records), x.origin, state.IP())
- for i, r := range records {
- l += dns.Len(r)
- if l > transferLength {
- ch <- &dns.Envelope{RR: records[j:i]}
- l = 0
- j = i
- }
- }
- if j < len(records) {
- ch <- &dns.Envelope{RR: records[j:]}
- }
-
- w.Hijack()
- // w.Close() // Client closes connection
- return dns.RcodeSuccess, nil
-}
-
-// Name implements the middleware.Hander interface.
-func (x Xfr) Name() string { return "xfr" }
-
-const transferLength = 1000 // Start a new envelop after message reaches this size in bytes. Intentionally small to test multi envelope parsing.
diff --git a/middleware/file/xfr_test.go b/middleware/file/xfr_test.go
deleted file mode 100644
index 69ad68e64..000000000
--- a/middleware/file/xfr_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package file
-
-import (
- "fmt"
- "strings"
-)
-
-func ExampleZone_All() {
- zone, err := Parse(strings.NewReader(dbMiekNL), testzone, "stdin", 0)
- if err != nil {
- return
- }
- records := zone.All()
- for _, r := range records {
- fmt.Printf("%+v\n", r)
- }
- // Output
- // xfr_test.go:15: miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400
- // xfr_test.go:15: www.miek.nl. 1800 IN CNAME a.miek.nl.
- // xfr_test.go:15: miek.nl. 1800 IN NS linode.atoom.net.
- // xfr_test.go:15: miek.nl. 1800 IN NS ns-ext.nlnetlabs.nl.
- // xfr_test.go:15: miek.nl. 1800 IN NS omval.tednet.nl.
- // xfr_test.go:15: miek.nl. 1800 IN NS ext.ns.whyscream.net.
- // xfr_test.go:15: miek.nl. 1800 IN MX 1 aspmx.l.google.com.
- // xfr_test.go:15: miek.nl. 1800 IN MX 5 alt1.aspmx.l.google.com.
- // xfr_test.go:15: miek.nl. 1800 IN MX 5 alt2.aspmx.l.google.com.
- // xfr_test.go:15: miek.nl. 1800 IN MX 10 aspmx2.googlemail.com.
- // xfr_test.go:15: miek.nl. 1800 IN MX 10 aspmx3.googlemail.com.
- // xfr_test.go:15: miek.nl. 1800 IN A 139.162.196.78
- // xfr_test.go:15: miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735
- // xfr_test.go:15: archive.miek.nl. 1800 IN CNAME a.miek.nl.
- // xfr_test.go:15: a.miek.nl. 1800 IN A 139.162.196.78
- // xfr_test.go:15: a.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735
-}
diff --git a/middleware/file/zone.go b/middleware/file/zone.go
deleted file mode 100644
index 3f3bcf0f7..000000000
--- a/middleware/file/zone.go
+++ /dev/null
@@ -1,190 +0,0 @@
-package file
-
-import (
- "fmt"
- "net"
- "path"
- "strings"
- "sync"
-
- "github.com/coredns/coredns/middleware/file/tree"
- "github.com/coredns/coredns/middleware/proxy"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-// Zone defines a structure that contains all data related to a DNS zone.
-type Zone struct {
- origin string
- origLen int
- file string
- *tree.Tree
- Apex Apex
-
- TransferTo []string
- StartupOnce sync.Once
- TransferFrom []string
- Expired *bool
-
- NoReload bool
- reloadMu sync.RWMutex
- ReloadShutdown chan bool
- Proxy proxy.Proxy // Proxy for looking up names during the resolution process
-}
-
-// Apex contains the apex records of a zone: SOA, NS and their potential signatures.
-type Apex struct {
- SOA *dns.SOA
- NS []dns.RR
- SIGSOA []dns.RR
- SIGNS []dns.RR
-}
-
-// NewZone returns a new zone.
-func NewZone(name, file string) *Zone {
- z := &Zone{
- origin: dns.Fqdn(name),
- origLen: dns.CountLabel(dns.Fqdn(name)),
- file: path.Clean(file),
- Tree: &tree.Tree{},
- Expired: new(bool),
- ReloadShutdown: make(chan bool),
- }
- *z.Expired = false
-
- return z
-}
-
-// Copy copies a zone.
-func (z *Zone) Copy() *Zone {
- z1 := NewZone(z.origin, z.file)
- z1.TransferTo = z.TransferTo
- z1.TransferFrom = z.TransferFrom
- z1.Expired = z.Expired
-
- z1.Apex = z.Apex
- return z1
-}
-
-// Insert inserts r into z.
-func (z *Zone) Insert(r dns.RR) error {
- r.Header().Name = strings.ToLower(r.Header().Name)
-
- switch h := r.Header().Rrtype; h {
- case dns.TypeNS:
- r.(*dns.NS).Ns = strings.ToLower(r.(*dns.NS).Ns)
-
- if r.Header().Name == z.origin {
- z.Apex.NS = append(z.Apex.NS, r)
- return nil
- }
- case dns.TypeSOA:
- r.(*dns.SOA).Ns = strings.ToLower(r.(*dns.SOA).Ns)
- r.(*dns.SOA).Mbox = strings.ToLower(r.(*dns.SOA).Mbox)
-
- z.Apex.SOA = r.(*dns.SOA)
- return nil
- case dns.TypeNSEC3, dns.TypeNSEC3PARAM:
- return fmt.Errorf("NSEC3 zone is not supported, dropping RR: %s for zone: %s", r.Header().Name, z.origin)
- case dns.TypeRRSIG:
- x := r.(*dns.RRSIG)
- switch x.TypeCovered {
- case dns.TypeSOA:
- z.Apex.SIGSOA = append(z.Apex.SIGSOA, x)
- return nil
- case dns.TypeNS:
- if r.Header().Name == z.origin {
- z.Apex.SIGNS = append(z.Apex.SIGNS, x)
- return nil
- }
- }
- case dns.TypeCNAME:
- r.(*dns.CNAME).Target = strings.ToLower(r.(*dns.CNAME).Target)
- case dns.TypeMX:
- r.(*dns.MX).Mx = strings.ToLower(r.(*dns.MX).Mx)
- case dns.TypeSRV:
- r.(*dns.SRV).Target = strings.ToLower(r.(*dns.SRV).Target)
- }
-
- z.Tree.Insert(r)
- return nil
-}
-
-// Delete deletes r from z.
-func (z *Zone) Delete(r dns.RR) { z.Tree.Delete(r) }
-
-// TransferAllowed checks if incoming request for transferring the zone is allowed according to the ACLs.
-func (z *Zone) TransferAllowed(state request.Request) bool {
- for _, t := range z.TransferTo {
- if t == "*" {
- return true
- }
- // If remote IP matches we accept.
- remote := state.IP()
- to, _, err := net.SplitHostPort(t)
- if err != nil {
- continue
- }
- if to == remote {
- return true
- }
- }
- // TODO(miek): future matching against IP/CIDR notations
- return false
-}
-
-// All returns all records from the zone, the first record will be the SOA record,
-// otionally followed by all RRSIG(SOA)s.
-func (z *Zone) All() []dns.RR {
- if !z.NoReload {
- z.reloadMu.RLock()
- defer z.reloadMu.RUnlock()
- }
-
- records := []dns.RR{}
- allNodes := z.Tree.All()
- for _, a := range allNodes {
- records = append(records, a.All()...)
- }
-
- if len(z.Apex.SIGNS) > 0 {
- records = append(z.Apex.SIGNS, records...)
- }
- records = append(z.Apex.NS, records...)
-
- if len(z.Apex.SIGSOA) > 0 {
- records = append(z.Apex.SIGSOA, records...)
- }
- return append([]dns.RR{z.Apex.SOA}, records...)
-}
-
-// Print prints the zone's tree to stdout.
-func (z *Zone) Print() {
- z.Tree.Print()
-}
-
-// NameFromRight returns the labels from the right, staring with the
-// origin and then i labels extra. When we are overshooting the name
-// the returned boolean is set to true.
-func (z *Zone) nameFromRight(qname string, i int) (string, bool) {
- if i <= 0 {
- return z.origin, false
- }
-
- for j := 1; j <= z.origLen; j++ {
- if _, shot := dns.PrevLabel(qname, j); shot {
- return qname, shot
- }
- }
-
- k := 0
- shot := false
- for j := 1; j <= i; j++ {
- k, shot = dns.PrevLabel(qname, j+z.origLen)
- if shot {
- return qname, shot
- }
- }
- return qname[k:], false
-}
diff --git a/middleware/file/zone_test.go b/middleware/file/zone_test.go
deleted file mode 100644
index c9ff174db..000000000
--- a/middleware/file/zone_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package file
-
-import "testing"
-
-func TestNameFromRight(t *testing.T) {
- z := NewZone("example.org.", "stdin")
-
- tests := []struct {
- in string
- labels int
- shot bool
- expected string
- }{
- {"example.org.", 0, false, "example.org."},
- {"a.example.org.", 0, false, "example.org."},
- {"a.example.org.", 1, false, "a.example.org."},
- {"a.example.org.", 2, true, "a.example.org."},
- {"a.b.example.org.", 2, false, "a.b.example.org."},
- }
-
- for i, tc := range tests {
- got, shot := z.nameFromRight(tc.in, tc.labels)
- if got != tc.expected {
- t.Errorf("Test %d: expected %s, got %s\n", i, tc.expected, got)
- }
- if shot != tc.shot {
- t.Errorf("Test %d: expected shot to be %t, got %t\n", i, tc.shot, shot)
- }
- }
-}
diff --git a/middleware/health/README.md b/middleware/health/README.md
deleted file mode 100644
index 195a480c0..000000000
--- a/middleware/health/README.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# health
-
-This module enables a simple health check endpoint. By default it will listen on port 8080.
-
-## Syntax
-
-~~~
-health [ADDRESS]
-~~~
-
-Optionally takes an address; the default is `:8080`. The health path is fixed to `/health`. The
-health endpoint returns a 200 response code and the word "OK" when CoreDNS is healthy. It returns
-a 503. *health* periodically (1s) polls middleware that exports health information. If any of the
-middleware signals that it is unhealthy, the server will go unhealthy too. Each middleware that
-supports health checks has a section "Health" in their README.
-
-## Examples
-
-Run another health endpoint on http://localhost:8091.
-
-~~~
-health localhost:8091
-~~~
diff --git a/middleware/health/health.go b/middleware/health/health.go
deleted file mode 100644
index 5e5c4c32f..000000000
--- a/middleware/health/health.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// Package health implements an HTTP handler that responds to health checks.
-package health
-
-import (
- "io"
- "log"
- "net"
- "net/http"
- "sync"
-)
-
-var once sync.Once
-
-type health struct {
- Addr string
-
- ln net.Listener
- mux *http.ServeMux
-
- // A slice of Healthers that the health middleware will poll every second for their health status.
- h []Healther
- sync.RWMutex
- ok bool // ok is the global boolean indicating an all healthy middleware stack
-}
-
-func (h *health) Startup() error {
- if h.Addr == "" {
- h.Addr = defAddr
- }
-
- once.Do(func() {
- ln, err := net.Listen("tcp", h.Addr)
- if err != nil {
- log.Printf("[ERROR] Failed to start health handler: %s", err)
- return
- }
-
- h.ln = ln
-
- h.mux = http.NewServeMux()
-
- h.mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
- if h.Ok() {
- w.WriteHeader(http.StatusOK)
- io.WriteString(w, ok)
- return
- }
- w.WriteHeader(http.StatusServiceUnavailable)
- })
-
- go func() {
- http.Serve(h.ln, h.mux)
- }()
- })
- return nil
-}
-
-func (h *health) Shutdown() error {
- if h.ln != nil {
- return h.ln.Close()
- }
- return nil
-}
-
-const (
- ok = "OK"
- defAddr = ":8080"
- path = "/health"
-)
diff --git a/middleware/health/health_test.go b/middleware/health/health_test.go
deleted file mode 100644
index ea22ad8b6..000000000
--- a/middleware/health/health_test.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package health
-
-// TODO(miek): enable again if middleware gets health check.
-/*
-func TestHealth(t *testing.T) {
- h := health{Addr: ":0"}
- h.h = append(h.h, &erratic.Erratic{})
-
- if err := h.Startup(); err != nil {
- t.Fatalf("Unable to startup the health server: %v", err)
- }
- defer h.Shutdown()
-
- // Reconstruct the http address based on the port allocated by operating system.
- address := fmt.Sprintf("http://%s%s", h.ln.Addr().String(), path)
-
- // Norhing set should be unhealthy
- response, err := http.Get(address)
- if err != nil {
- t.Fatalf("Unable to query %s: %v", address, err)
- }
- if response.StatusCode != 503 {
- t.Errorf("Invalid status code: expecting '503', got '%d'", response.StatusCode)
- }
- response.Body.Close()
-
- // Make healthy
- h.Poll()
-
- response, err = http.Get(address)
- if err != nil {
- t.Fatalf("Unable to query %s: %v", address, err)
- }
- if response.StatusCode != 200 {
- t.Errorf("Invalid status code: expecting '200', got '%d'", response.StatusCode)
- }
- content, err := ioutil.ReadAll(response.Body)
- if err != nil {
- t.Fatalf("Unable to get response body from %s: %v", address, err)
- }
- response.Body.Close()
-
- if string(content) != ok {
- t.Errorf("Invalid response body: expecting 'OK', got '%s'", string(content))
- }
-}
-*/
diff --git a/middleware/health/healther.go b/middleware/health/healther.go
deleted file mode 100644
index 1b794e03b..000000000
--- a/middleware/health/healther.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package health
-
-// Healther interface needs to be implemented by each middleware willing to
-// provide healthhceck information to the health middleware. As a second step
-// the middleware needs to registered against the health middleware, by addding
-// it to healthers map. Note this method should return quickly, i.e. just
-// checking a boolean status, as it is called every second from the health
-// middleware.
-type Healther interface {
- // Health returns a boolean indicating the health status of a middleware.
- // False indicates unhealthy.
- Health() bool
-}
-
-// Ok returns the global health status of all middleware configured in this server.
-func (h *health) Ok() bool {
- h.RLock()
- defer h.RUnlock()
- return h.ok
-}
-
-// SetOk sets the global health status of all middleware configured in this server.
-func (h *health) SetOk(ok bool) {
- h.Lock()
- defer h.Unlock()
- h.ok = ok
-}
-
-// poll polls all healthers and sets the global state.
-func (h *health) poll() {
- for _, m := range h.h {
- if !m.Health() {
- h.SetOk(false)
- return
- }
- }
- h.SetOk(true)
-}
-
-// Middleware that implements the Healther interface.
-// TODO(miek): none yet.
-var healthers = map[string]bool{}
diff --git a/middleware/health/setup.go b/middleware/health/setup.go
deleted file mode 100644
index bf465bd74..000000000
--- a/middleware/health/setup.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package health
-
-import (
- "net"
- "time"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("health", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- addr, err := healthParse(c)
- if err != nil {
- return middleware.Error("health", err)
- }
-
- h := &health{Addr: addr}
-
- c.OnStartup(func() error {
- for he := range healthers {
- m := dnsserver.GetConfig(c).Handler(he)
- if x, ok := m.(Healther); ok {
- h.h = append(h.h, x)
- }
- }
- return nil
- })
-
- c.OnStartup(func() error {
- h.poll()
- go func() {
- for {
- <-time.After(1 * time.Second)
- h.poll()
- }
- }()
- return nil
- })
-
- c.OnStartup(h.Startup)
- c.OnFinalShutdown(h.Shutdown)
-
- // Don't do AddMiddleware, as health is not *really* a middleware just a separate webserver running.
- return nil
-}
-
-func healthParse(c *caddy.Controller) (string, error) {
- addr := ""
- for c.Next() {
- args := c.RemainingArgs()
-
- switch len(args) {
- case 0:
- case 1:
- addr = args[0]
- if _, _, e := net.SplitHostPort(addr); e != nil {
- return "", e
- }
- default:
- return "", c.ArgErr()
- }
- }
- return addr, nil
-}
diff --git a/middleware/health/setup_test.go b/middleware/health/setup_test.go
deleted file mode 100644
index 87f4fc5fd..000000000
--- a/middleware/health/setup_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package health
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestSetupHealth(t *testing.T) {
- tests := []struct {
- input string
- shouldErr bool
- }{
- {`health`, false},
- {`health localhost:1234`, false},
- {`health bla:a`, false},
- {`health bla`, true},
- {`health bla bla`, true},
- }
-
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- _, err := healthParse(c)
-
- if test.shouldErr && err == nil {
- t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input)
- }
-
- if err != nil {
- if !test.shouldErr {
- t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
- }
- }
- }
-}
diff --git a/middleware/hosts/README.md b/middleware/hosts/README.md
deleted file mode 100644
index 91d340cb5..000000000
--- a/middleware/hosts/README.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# hosts
-
-*hosts* enables serving zone data from a `/etc/hosts` style file.
-
-The hosts middleware is useful for serving zones from a /etc/hosts file. It serves from a preloaded
-file that exists on disk. It checks the file for changes and updates the zones accordingly. This
-middleware only supports A, AAAA, and PTR records. The hosts middleware can be used with readily
-available hosts files that block access to advertising servers.
-
-## Syntax
-
-~~~
-hosts [FILE [ZONES...]] {
- fallthrough
-}
-~~~
-
-* **FILE** the hosts file to read and parse. If the path is relative the path from the *root*
- directive will be prepended to it. Defaults to /etc/hosts if omitted
-* **ZONES** zones it should be authoritative for. If empty, the zones from the configuration block
- are used.
-* `fallthrough` If zone matches and no record can be generated, pass request to the next middleware.
-
-## Examples
-
-Load `/etc/hosts` file.
-
-~~~
-hosts
-~~~
-
-Load `example.hosts` file in the current directory.
-
-~~~
-hosts example.hosts
-~~~
-
-Load example.hosts file and only serve example.org and example.net from it and fall through to the
-next middleware if query doesn't match.
-
-~~~
-hosts example.hosts example.org example.net {
- fallthrough
-}
-~~~
diff --git a/middleware/hosts/hosts.go b/middleware/hosts/hosts.go
deleted file mode 100644
index 28efc47ec..000000000
--- a/middleware/hosts/hosts.go
+++ /dev/null
@@ -1,136 +0,0 @@
-package hosts
-
-import (
- "net"
-
- "golang.org/x/net/context"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/request"
- "github.com/miekg/dns"
-)
-
-// Hosts is the middleware handler
-type Hosts struct {
- Next middleware.Handler
- *Hostsfile
-
- Fallthrough bool
-}
-
-// ServeDNS implements the middleware.Handle interface.
-func (h Hosts) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- state := request.Request{W: w, Req: r}
- qname := state.Name()
-
- answers := []dns.RR{}
-
- zone := middleware.Zones(h.Origins).Matches(qname)
- if zone == "" {
- // PTR zones don't need to be specified in Origins
- if state.Type() != "PTR" {
- // If this doesn't match we need to fall through regardless of h.Fallthrough
- return middleware.NextOrFailure(h.Name(), h.Next, ctx, w, r)
- }
- }
-
- switch state.QType() {
- case dns.TypePTR:
- names := h.LookupStaticAddr(dnsutil.ExtractAddressFromReverse(qname))
- if len(names) == 0 {
- // If this doesn't match we need to fall through regardless of h.Fallthrough
- return middleware.NextOrFailure(h.Name(), h.Next, ctx, w, r)
- }
- answers = h.ptr(qname, names)
- case dns.TypeA:
- ips := h.LookupStaticHostV4(qname)
- answers = a(qname, ips)
- case dns.TypeAAAA:
- ips := h.LookupStaticHostV6(qname)
- answers = aaaa(qname, ips)
- }
-
- if len(answers) == 0 {
- if h.Fallthrough {
- return middleware.NextOrFailure(h.Name(), h.Next, ctx, w, r)
- }
- if !h.otherRecordsExist(state.QType(), qname) {
- return dns.RcodeNameError, nil
- }
- }
-
- m := new(dns.Msg)
- m.SetReply(r)
- m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
- m.Answer = answers
-
- state.SizeAndDo(m)
- m, _ = state.Scrub(m)
- w.WriteMsg(m)
- return dns.RcodeSuccess, nil
-}
-
-func (h Hosts) otherRecordsExist(qtype uint16, qname string) bool {
- switch qtype {
- case dns.TypeA:
- if len(h.LookupStaticHostV6(qname)) > 0 {
- return true
- }
- case dns.TypeAAAA:
- if len(h.LookupStaticHostV4(qname)) > 0 {
- return true
- }
- default:
- if len(h.LookupStaticHostV4(qname)) > 0 {
- return true
- }
- if len(h.LookupStaticHostV6(qname)) > 0 {
- return true
- }
- }
- return false
-
-}
-
-// Name implements the middleware.Handle interface.
-func (h Hosts) Name() string { return "hosts" }
-
-// a takes a slice of net.IPs and returns a slice of A RRs.
-func a(zone string, ips []net.IP) []dns.RR {
- answers := []dns.RR{}
- for _, ip := range ips {
- r := new(dns.A)
- r.Hdr = dns.RR_Header{Name: zone, Rrtype: dns.TypeA,
- Class: dns.ClassINET, Ttl: 3600}
- r.A = ip
- answers = append(answers, r)
- }
- return answers
-}
-
-// aaaa takes a slice of net.IPs and returns a slice of AAAA RRs.
-func aaaa(zone string, ips []net.IP) []dns.RR {
- answers := []dns.RR{}
- for _, ip := range ips {
- r := new(dns.AAAA)
- r.Hdr = dns.RR_Header{Name: zone, Rrtype: dns.TypeAAAA,
- Class: dns.ClassINET, Ttl: 3600}
- r.AAAA = ip
- answers = append(answers, r)
- }
- return answers
-}
-
-// ptr takes a slice of host names and filters out the ones that aren't in Origins, if specified, and returns a slice of PTR RRs.
-func (h *Hosts) ptr(zone string, names []string) []dns.RR {
- answers := []dns.RR{}
- for _, n := range names {
- r := new(dns.PTR)
- r.Hdr = dns.RR_Header{Name: zone, Rrtype: dns.TypePTR,
- Class: dns.ClassINET, Ttl: 3600}
- r.Ptr = dns.Fqdn(n)
- answers = append(answers, r)
- }
- return answers
-}
diff --git a/middleware/hosts/hosts_test.go b/middleware/hosts/hosts_test.go
deleted file mode 100644
index b6ebaa43a..000000000
--- a/middleware/hosts/hosts_test.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package hosts
-
-import (
- "strings"
- "testing"
- "time"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func TestLookupA(t *testing.T) {
- h := Hosts{Next: test.ErrorHandler(), Hostsfile: &Hostsfile{expire: time.Now().Add(1 * time.Hour), Origins: []string{"."}}}
- h.Parse(strings.NewReader(hostsExample))
-
- ctx := context.TODO()
-
- for _, tc := range hostsTestCases {
- m := tc.Msg()
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := h.ServeDNS(ctx, rec, m)
- if err != nil {
- t.Errorf("Expected no error, got %v\n", err)
- return
- }
-
- resp := rec.Msg
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-var hostsTestCases = []test.Case{
- {
- Qname: "example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- test.A("example.org. 3600 IN A 10.0.0.1"),
- },
- },
- {
- Qname: "localhost.", Qtype: dns.TypeAAAA,
- Answer: []dns.RR{
- test.AAAA("localhost. 3600 IN AAAA ::1"),
- },
- },
- {
- Qname: "1.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
- Answer: []dns.RR{
- test.PTR("1.0.0.10.in-addr.arpa. 3600 PTR example.org."),
- },
- },
- {
- Qname: "1.0.0.127.in-addr.arpa.", Qtype: dns.TypePTR,
- Answer: []dns.RR{
- test.PTR("1.0.0.127.in-addr.arpa. 3600 PTR localhost."),
- test.PTR("1.0.0.127.in-addr.arpa. 3600 PTR localhost.domain."),
- },
- },
- {
- Qname: "example.org.", Qtype: dns.TypeAAAA,
- Answer: []dns.RR{},
- },
- {
- Qname: "example.org.", Qtype: dns.TypeMX,
- Answer: []dns.RR{},
- },
-}
-
-const hostsExample = `
-127.0.0.1 localhost localhost.domain
-::1 localhost localhost.domain
-10.0.0.1 example.org`
diff --git a/middleware/hosts/hostsfile.go b/middleware/hosts/hostsfile.go
deleted file mode 100644
index a4f4a017d..000000000
--- a/middleware/hosts/hostsfile.go
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file is a modified version of net/hosts.go from the golang repo
-
-package hosts
-
-import (
- "bufio"
- "bytes"
- "io"
- "net"
- "os"
- "strings"
- "sync"
- "time"
-
- "github.com/coredns/coredns/middleware"
-)
-
-const cacheMaxAge = 5 * time.Second
-
-func parseLiteralIP(addr string) net.IP {
- if i := strings.Index(addr, "%"); i >= 0 {
- // discard ipv6 zone
- addr = addr[0:i]
- }
-
- return net.ParseIP(addr)
-}
-
-func absDomainName(b string) string {
- return middleware.Name(b).Normalize()
-}
-
-// Hostsfile contains known host entries.
-type Hostsfile struct {
- sync.Mutex
-
- // list of zones we are authoritive for
- Origins []string
-
- // Key for the list of literal IP addresses must be a host
- // name. It would be part of DNS labels, a FQDN or an absolute
- // FQDN.
- // For now the key is converted to lower case for convenience.
- byNameV4 map[string][]net.IP
- byNameV6 map[string][]net.IP
-
- // Key for the list of host names must be a literal IP address
- // including IPv6 address with zone identifier.
- // We don't support old-classful IP address notation.
- byAddr map[string][]string
-
- expire time.Time
- path string
- mtime time.Time
- size int64
-}
-
-// ReadHosts determines if the cached data needs to be updated based on the size and modification time of the hostsfile.
-func (h *Hostsfile) ReadHosts() {
- now := time.Now()
-
- if now.Before(h.expire) && len(h.byAddr) > 0 {
- return
- }
- stat, err := os.Stat(h.path)
- if err == nil && h.mtime.Equal(stat.ModTime()) && h.size == stat.Size() {
- h.expire = now.Add(cacheMaxAge)
- return
- }
-
- var file *os.File
- if file, _ = os.Open(h.path); file == nil {
- return
- }
- defer file.Close()
-
- h.Parse(file)
-
- // Update the data cache.
- h.expire = now.Add(cacheMaxAge)
- h.mtime = stat.ModTime()
- h.size = stat.Size()
-}
-
-// Parse reads the hostsfile and populates the byName and byAddr maps.
-func (h *Hostsfile) Parse(file io.Reader) {
- hsv4 := make(map[string][]net.IP)
- hsv6 := make(map[string][]net.IP)
- is := make(map[string][]string)
-
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- line := scanner.Bytes()
- if i := bytes.Index(line, []byte{'#'}); i >= 0 {
- // Discard comments.
- line = line[0:i]
- }
- f := bytes.Fields(line)
- if len(f) < 2 {
- continue
- }
- addr := parseLiteralIP(string(f[0]))
- if addr == nil {
- continue
- }
- ver := ipVersion(string(f[0]))
- for i := 1; i < len(f); i++ {
- name := absDomainName(string(f[i]))
- if middleware.Zones(h.Origins).Matches(name) == "" {
- // name is not in Origins
- continue
- }
- switch ver {
- case 4:
- hsv4[name] = append(hsv4[name], addr)
- case 6:
- hsv6[name] = append(hsv6[name], addr)
- default:
- continue
- }
- is[addr.String()] = append(is[addr.String()], name)
- }
- }
- h.byNameV4 = hsv4
- h.byNameV6 = hsv6
- h.byAddr = is
-}
-
-// ipVersion returns what IP version was used textually
-func ipVersion(s string) int {
- for i := 0; i < len(s); i++ {
- switch s[i] {
- case '.':
- return 4
- case ':':
- return 6
- }
- }
- return 0
-}
-
-// LookupStaticHostV4 looks up the IPv4 addresses for the given host from the hosts file.
-func (h *Hostsfile) LookupStaticHostV4(host string) []net.IP {
- h.Lock()
- defer h.Unlock()
- h.ReadHosts()
- if len(h.byNameV4) != 0 {
- if ips, ok := h.byNameV4[absDomainName(host)]; ok {
- ipsCp := make([]net.IP, len(ips))
- copy(ipsCp, ips)
- return ipsCp
- }
- }
- return nil
-}
-
-// LookupStaticHostV6 looks up the IPv6 addresses for the given host from the hosts file.
-func (h *Hostsfile) LookupStaticHostV6(host string) []net.IP {
- h.Lock()
- defer h.Unlock()
- h.ReadHosts()
- if len(h.byNameV6) != 0 {
- if ips, ok := h.byNameV6[absDomainName(host)]; ok {
- ipsCp := make([]net.IP, len(ips))
- copy(ipsCp, ips)
- return ipsCp
- }
- }
- return nil
-}
-
-// LookupStaticAddr looks up the hosts for the given address from the hosts file.
-func (h *Hostsfile) LookupStaticAddr(addr string) []string {
- h.Lock()
- defer h.Unlock()
- h.ReadHosts()
- addr = parseLiteralIP(addr).String()
- if addr == "" {
- return nil
- }
- if len(h.byAddr) != 0 {
- if hosts, ok := h.byAddr[addr]; ok {
- hostsCp := make([]string, len(hosts))
- copy(hostsCp, hosts)
- return hostsCp
- }
- }
- return nil
-}
diff --git a/middleware/hosts/hostsfile_test.go b/middleware/hosts/hostsfile_test.go
deleted file mode 100644
index 65841fa42..000000000
--- a/middleware/hosts/hostsfile_test.go
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package hosts
-
-import (
- "net"
- "reflect"
- "strings"
- "testing"
- "time"
-)
-
-func testHostsfile(file string) *Hostsfile {
- h := &Hostsfile{expire: time.Now().Add(1 * time.Hour), Origins: []string{"."}}
- h.Parse(strings.NewReader(file))
- return h
-}
-
-type staticHostEntry struct {
- in string
- v4 []string
- v6 []string
-}
-
-var (
- hosts = `255.255.255.255 broadcasthost
- 127.0.0.2 odin
- 127.0.0.3 odin # inline comment
- ::2 odin
- 127.1.1.1 thor
- # aliases
- 127.1.1.2 ullr ullrhost
- fe80::1%lo0 localhost
- # Bogus entries that must be ignored.
- 123.123.123 loki
- 321.321.321.321`
- singlelinehosts = `127.0.0.2 odin`
- ipv4hosts = `# See https://tools.ietf.org/html/rfc1123.
- #
- # The literal IPv4 address parser in the net package is a relaxed
- # one. It may accept a literal IPv4 address in dotted-decimal notation
- # with leading zeros such as "001.2.003.4".
-
- # internet address and host name
- 127.0.0.1 localhost # inline comment separated by tab
- 127.000.000.002 localhost # inline comment separated by space
-
- # internet address, host name and aliases
- 127.000.000.003 localhost localhost.localdomain`
- ipv6hosts = `# See https://tools.ietf.org/html/rfc5952, https://tools.ietf.org/html/rfc4007.
-
- # internet address and host name
- ::1 localhost # inline comment separated by tab
- fe80:0000:0000:0000:0000:0000:0000:0001 localhost # inline comment separated by space
-
- # internet address with zone identifier and host name
- fe80:0000:0000:0000:0000:0000:0000:0002%lo0 localhost
-
- # internet address, host name and aliases
- fe80::3%lo0 localhost localhost.localdomain`
- casehosts = `127.0.0.1 PreserveMe PreserveMe.local
- ::1 PreserveMe PreserveMe.local`
-)
-
-var lookupStaticHostTests = []struct {
- file string
- ents []staticHostEntry
-}{
- {
- hosts,
- []staticHostEntry{
- {"odin", []string{"127.0.0.2", "127.0.0.3"}, []string{"::2"}},
- {"thor", []string{"127.1.1.1"}, []string{}},
- {"ullr", []string{"127.1.1.2"}, []string{}},
- {"ullrhost", []string{"127.1.1.2"}, []string{}},
- {"localhost", []string{}, []string{"fe80::1"}},
- },
- },
- {
- singlelinehosts, // see golang.org/issue/6646
- []staticHostEntry{
- {"odin", []string{"127.0.0.2"}, []string{}},
- },
- },
- {
- ipv4hosts,
- []staticHostEntry{
- {"localhost", []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, []string{}},
- {"localhost.localdomain", []string{"127.0.0.3"}, []string{}},
- },
- },
- {
- ipv6hosts,
- []staticHostEntry{
- {"localhost", []string{}, []string{"::1", "fe80::1", "fe80::2", "fe80::3"}},
- {"localhost.localdomain", []string{}, []string{"fe80::3"}},
- },
- },
- {
- casehosts,
- []staticHostEntry{
- {"PreserveMe", []string{"127.0.0.1"}, []string{"::1"}},
- {"PreserveMe.local", []string{"127.0.0.1"}, []string{"::1"}},
- },
- },
-}
-
-func TestLookupStaticHost(t *testing.T) {
-
- for _, tt := range lookupStaticHostTests {
- h := testHostsfile(tt.file)
- for _, ent := range tt.ents {
- testStaticHost(t, ent, h)
- }
- }
-}
-
-func testStaticHost(t *testing.T, ent staticHostEntry, h *Hostsfile) {
- ins := []string{ent.in, absDomainName(ent.in), strings.ToLower(ent.in), strings.ToUpper(ent.in)}
- for k, in := range ins {
- addrsV4 := h.LookupStaticHostV4(in)
- if len(addrsV4) != len(ent.v4) {
- t.Fatalf("%d, lookupStaticHostV4(%s) = %v; want %v", k, in, addrsV4, ent.v4)
- }
- for i, v4 := range addrsV4 {
- if v4.String() != ent.v4[i] {
- t.Fatalf("%d, lookupStaticHostV4(%s) = %v; want %v", k, in, addrsV4, ent.v4)
- }
- }
- addrsV6 := h.LookupStaticHostV6(in)
- if len(addrsV6) != len(ent.v6) {
- t.Fatalf("%d, lookupStaticHostV6(%s) = %v; want %v", k, in, addrsV6, ent.v6)
- }
- for i, v6 := range addrsV6 {
- if v6.String() != ent.v6[i] {
- t.Fatalf("%d, lookupStaticHostV6(%s) = %v; want %v", k, in, addrsV6, ent.v6)
- }
- }
- }
-}
-
-type staticIPEntry struct {
- in string
- out []string
-}
-
-var lookupStaticAddrTests = []struct {
- file string
- ents []staticIPEntry
-}{
- {
- hosts,
- []staticIPEntry{
- {"255.255.255.255", []string{"broadcasthost"}},
- {"127.0.0.2", []string{"odin"}},
- {"127.0.0.3", []string{"odin"}},
- {"::2", []string{"odin"}},
- {"127.1.1.1", []string{"thor"}},
- {"127.1.1.2", []string{"ullr", "ullrhost"}},
- {"fe80::1", []string{"localhost"}},
- },
- },
- {
- singlelinehosts, // see golang.org/issue/6646
- []staticIPEntry{
- {"127.0.0.2", []string{"odin"}},
- },
- },
- {
- ipv4hosts, // see golang.org/issue/8996
- []staticIPEntry{
- {"127.0.0.1", []string{"localhost"}},
- {"127.0.0.2", []string{"localhost"}},
- {"127.0.0.3", []string{"localhost", "localhost.localdomain"}},
- },
- },
- {
- ipv6hosts, // see golang.org/issue/8996
- []staticIPEntry{
- {"::1", []string{"localhost"}},
- {"fe80::1", []string{"localhost"}},
- {"fe80::2", []string{"localhost"}},
- {"fe80::3", []string{"localhost", "localhost.localdomain"}},
- },
- },
- {
- casehosts, // see golang.org/issue/12806
- []staticIPEntry{
- {"127.0.0.1", []string{"PreserveMe", "PreserveMe.local"}},
- {"::1", []string{"PreserveMe", "PreserveMe.local"}},
- },
- },
-}
-
-func TestLookupStaticAddr(t *testing.T) {
- for _, tt := range lookupStaticAddrTests {
- h := testHostsfile(tt.file)
- for _, ent := range tt.ents {
- testStaticAddr(t, ent, h)
- }
- }
-}
-
-func testStaticAddr(t *testing.T, ent staticIPEntry, h *Hostsfile) {
- hosts := h.LookupStaticAddr(ent.in)
- for i := range ent.out {
- ent.out[i] = absDomainName(ent.out[i])
- }
- if !reflect.DeepEqual(hosts, ent.out) {
- t.Errorf("%s, lookupStaticAddr(%s) = %v; want %v", h.path, ent.in, hosts, h)
- }
-}
-
-func TestHostCacheModification(t *testing.T) {
- // Ensure that programs can't modify the internals of the host cache.
- // See https://github.com/golang/go/issues/14212.
-
- h := testHostsfile(ipv4hosts)
- ent := staticHostEntry{"localhost", []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, []string{}}
- testStaticHost(t, ent, h)
- // Modify the addresses return by lookupStaticHost.
- addrs := h.LookupStaticHostV6(ent.in)
- for i := range addrs {
- addrs[i] = net.IPv4zero
- }
- testStaticHost(t, ent, h)
-
- h = testHostsfile(ipv6hosts)
- entip := staticIPEntry{"::1", []string{"localhost"}}
- testStaticAddr(t, entip, h)
- // Modify the hosts return by lookupStaticAddr.
- hosts := h.LookupStaticAddr(entip.in)
- for i := range hosts {
- hosts[i] += "junk"
- }
- testStaticAddr(t, entip, h)
-}
diff --git a/middleware/hosts/setup.go b/middleware/hosts/setup.go
deleted file mode 100644
index 38458541c..000000000
--- a/middleware/hosts/setup.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package hosts
-
-import (
- "log"
- "os"
- "path"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("hosts", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- h, err := hostsParse(c)
- if err != nil {
- return middleware.Error("hosts", err)
- }
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- h.Next = next
- return h
- })
-
- return nil
-}
-
-func hostsParse(c *caddy.Controller) (Hosts, error) {
- var h = Hosts{
- Hostsfile: &Hostsfile{path: "/etc/hosts"},
- }
- defer h.ReadHosts()
-
- config := dnsserver.GetConfig(c)
-
- for c.Next() {
- args := c.RemainingArgs()
- if len(args) >= 1 {
- h.path = args[0]
- args = args[1:]
-
- if !path.IsAbs(h.path) && config.Root != "" {
- h.path = path.Join(config.Root, h.path)
- }
- _, err := os.Stat(h.path)
- if err != nil {
- if os.IsNotExist(err) {
- log.Printf("[WARNING] File does not exist: %s", h.path)
- } else {
- return h, c.Errf("unable to access hosts file '%s': %v", h.path, err)
- }
- }
- }
-
- origins := make([]string, len(c.ServerBlockKeys))
- copy(origins, c.ServerBlockKeys)
- if len(args) > 0 {
- origins = args
- }
-
- for i := range origins {
- origins[i] = middleware.Host(origins[i]).Normalize()
- }
- h.Origins = origins
-
- for c.NextBlock() {
- switch c.Val() {
- case "fallthrough":
- args := c.RemainingArgs()
- if len(args) == 0 {
- h.Fallthrough = true
- continue
- }
- return h, c.ArgErr()
- default:
- return h, c.Errf("unknown property '%s'", c.Val())
- }
- }
- }
- return h, nil
-}
diff --git a/middleware/hosts/setup_test.go b/middleware/hosts/setup_test.go
deleted file mode 100644
index a4c95b1c6..000000000
--- a/middleware/hosts/setup_test.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package hosts
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestHostsParse(t *testing.T) {
- tests := []struct {
- inputFileRules string
- shouldErr bool
- expectedPath string
- expectedOrigins []string
- expectedFallthrough bool
- }{
- {
- `hosts
-`,
- false, "/etc/hosts", nil, false,
- },
- {
- `hosts /tmp`,
- false, "/tmp", nil, false,
- },
- {
- `hosts /etc/hosts miek.nl.`,
- false, "/etc/hosts", []string{"miek.nl."}, false,
- },
- {
- `hosts /etc/hosts miek.nl. pun.gent.`,
- false, "/etc/hosts", []string{"miek.nl.", "pun.gent."}, false,
- },
- {
- `hosts {
- fallthrough
- }`,
- false, "/etc/hosts", nil, true,
- },
- {
- `hosts /tmp {
- fallthrough
- }`,
- false, "/tmp", nil, true,
- },
- {
- `hosts /etc/hosts miek.nl. {
- fallthrough
- }`,
- false, "/etc/hosts", []string{"miek.nl."}, true,
- },
- {
- `hosts /etc/hosts miek.nl 10.0.0.9/8 {
- fallthrough
- }`,
- false, "/etc/hosts", []string{"miek.nl.", "10.in-addr.arpa."}, true,
- },
- }
-
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.inputFileRules)
- h, err := hostsParse(c)
-
- if err == nil && test.shouldErr {
- t.Fatalf("Test %d expected errors, but got no error", i)
- } else if err != nil && !test.shouldErr {
- t.Fatalf("Test %d expected no errors, but got '%v'", i, err)
- } else if !test.shouldErr {
- if h.path != test.expectedPath {
- t.Fatalf("Test %d expected %v, got %v", i, test.expectedPath, h.path)
- }
- } else {
- if h.Fallthrough != test.expectedFallthrough {
- t.Fatalf("Test %d expected fallthrough of %v, got %v", i, test.expectedFallthrough, h.Fallthrough)
- }
- if len(h.Origins) != len(test.expectedOrigins) {
- t.Fatalf("Test %d expected %v, got %v", i, test.expectedOrigins, h.Origins)
- }
- for j, name := range test.expectedOrigins {
- if h.Origins[j] != name {
- t.Fatalf("Test %d expected %v for %d th zone, got %v", i, name, j, h.Origins[j])
- }
- }
- }
- }
-}
diff --git a/middleware/kubernetes/DEV-README.md b/middleware/kubernetes/DEV-README.md
deleted file mode 100644
index 4f652b578..000000000
--- a/middleware/kubernetes/DEV-README.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Basic Setup for Development and Testing
-
-## Launch Kubernetes
-
-To run the tests, you'll need a private, live Kubernetes cluster. If you don't have one,
-you can try out [minikube](https://github.com/kubernetes/minikube), which is
-also available via Homebrew for OS X users.
-
-## Configure Test Data
-
-The test data is all in [this manifest](https://github.com/coredns/coredns/blob/master/.travis/kubernetes/dns-test.yaml)
-and you can load it with `kubectl apply -f`. It will create a couple namespaces and some services.
-For the tests to pass, you should not create anything else in the cluster.
-
-## Proxy the API Server
-
-Assuming your Kuberentes API server isn't running on http://localhost:8080, you will need to proxy from that
-port to your cluster. You can do this with `kubectl proxy --port 8080`.
-
-## Run CoreDNS Kubernetes Tests
-
-Now you can run the tests locally, for example:
-
-~~~
-$ cd $GOPATH/src/github.com/coredns/coredns/test
-$ go test -v -tags k8s
-~~~
-
-# Implementation Notes/Ideas
-
-* Additional features:
- * Implement IP selection and ordering (internal/external). Related to
- wildcards and SkyDNS use of CNAMES.
- * Expose arbitrary kubernetes repository data as TXT records?
-* DNS Correctness
- * Do we need to generate synthetic zone records for namespaces?
- * Do we need to generate synthetic zone records for the skydns synthetic zones?
-* Test cases
- * Test with CoreDNS caching. CoreDNS caching for DNS response is working
- using the `cache` directive. Tested working using 20s cache timeout
- and A-record queries. Automate testing with cache in place.
- * Automate CoreDNS performance tests. Initially for zone files, and for
- pre-loaded k8s API cache. With and without CoreDNS response caching.
diff --git a/middleware/kubernetes/README.md b/middleware/kubernetes/README.md
deleted file mode 100644
index 3e199c93a..000000000
--- a/middleware/kubernetes/README.md
+++ /dev/null
@@ -1,167 +0,0 @@
-# kubernetes
-
-The *kubernetes* middleware enables the reading zone data from a Kubernetes cluster. It implements
-the [Kubernetes DNS-Based Service Discovery
-Specification](https://github.com/kubernetes/dns/blob/master/docs/specification.md).
-
-CoreDNS running the kubernetes middleware can be used as a replacement of kube-dns in a kubernetes
-cluster. See the [deployment](https://github.com/coredns/deployment) repository for details on [how
-to deploy CoreDNS in Kubernetes](https://github.com/coredns/deployment/tree/master/kubernetes).
-
-[stubDomains](http://blog.kubernetes.io/2017/04/configuring-private-dns-zones-upstream-nameservers-kubernetes.html)
-are implemented via the *proxy* middleware.
-
-## Syntax
-
-~~~
-kubernetes [ZONES...]
-~~~
-
-With only the directive specified, the *kubernetes* middleware will default to the zone specified in
-the server's block. It will handle all queries in that zone and connect to Kubernetes in-cluster. It
-will not provide PTR records for services, or A records for pods. If **ZONES** is used it specifies
-all the zones the middleware should be authoritative for.
-
-```
-kubernetes [ZONES...] {
- resyncperiod DURATION
- endpoint URL
- tls CERT KEY CACERT
- namespaces NAMESPACE...
- labels EXPRESSION
- pods POD-MODE
- upstream ADDRESS...
- ttl TTL
- fallthrough
-}
-```
-* `resyncperiod` specifies the Kubernetes data API **DURATION** period.
-* `endpoint` specifies the **URL** for a remove k8s API endpoint.
- If omitted, it will connect to k8s in-cluster using the cluster service account.
- Multiple k8s API endpoints could be specified, separated by `,`s, e.g.
- `endpoint http://k8s-endpoint1:8080,http://k8s-endpoint2:8080`. CoreDNS
- will automatically perform a healthcheck and proxy to the healthy k8s API endpoint.
-* `tls` **CERT** **KEY** **CACERT** are the TLS cert, key and the CA cert file names for remote k8s connection.
- This option is ignored if connecting in-cluster (i.e. endpoint is not specified).
-* `namespaces` **NAMESPACE [NAMESPACE...]**, exposed only the k8s namespaces listed.
- If this option is omitted all namespaces are exposed
-* `labels` **EXPRESSION** only exposes the records for Kubernetes objects that match this label selector.
- The label selector syntax is described in the
- [Kubernetes User Guide - Labels](http://kubernetes.io/docs/user-guide/labels/). An example that
- only exposes objects labeled as "application=nginx" in the "staging" or "qa" environments, would
- use: `labels environment in (staging, qa),application=nginx`.
-* `pods` **POD-MODE** sets the mode for handling IP-based pod A records, e.g.
- `1-2-3-4.ns.pod.cluster.local. in A 1.2.3.4`.
- This option is provided to facilitate use of SSL certs when connecting directly to pods. Valid
- values for **POD-MODE**:
-
- * `disabled`: Default. Do not process pod requests, always returning `NXDOMAIN`
- * `insecure`: Always return an A record with IP from request (without checking k8s). This option
- is is vulnerable to abuse if used maliciously in conjunction with wildcard SSL certs. This
- option is provided for backward compatibility with kube-dns.
- * `verified`: Return an A record if there exists a pod in same namespace with matching IP. This
- option requires substantially more memory than in insecure mode, since it will maintain a watch
- on all pods.
-
-* `upstream` **ADDRESS [ADDRESS...]** defines the upstream resolvers used for resolving services
- that point to external hosts (External Services). **ADDRESS** can be an ip, an ip:port, or a path
- to a file structured like resolv.conf.
-* `ttl` allows you to set a custom TTL for responses. The default (and allowed minimum) is to use
- 5 seconds, the maximum is capped at 3600 seconds.
-* `fallthrough` If a query for a record in the cluster zone results in NXDOMAIN, normally that is
- what the response will be. However, if you specify this option, the query will instead be passed
- on down the middleware chain, which can include another middleware to handle the query.
-
-## Examples
-
-Handle all queries in the `cluster.local` zone. Connect to Kubernetes in-cluster.
-Also handle all `PTR` requests for `10.0.0.0/16` . Verify the existence of pods when answering pod
-requests. Resolve upstream records against `10.102.3.10`. Note we show the entire server block
-here:
-
-~~~ txt
-10.0.0.0/16 cluster.local {
- kubernetes {
- pods verified
- upstream 10.102.3.10:53
- }
-}
-~~~
-
-Or you can selectively expose some namespaces:
-
-~~~ txt
-kubernetes cluster.local {
- namespaces test staging
-}
-~~~
-
-Connect to Kubernetes with CoreDNS running outside the cluster:
-
-~~~ txt
-kubernetes cluster.local {
- endpoint https://k8s-endpoint:8443
- tls cert key cacert
-}
-~~~
-
-Here we use the *proxy* middleware to implement stubDomains that forwards `example.org` and
-`example.com` to another nameserver.
-
-~~~ txt
-cluster.local {
- kubernetes {
- endpoint https://k8s-endpoint:8443
- tls cert key cacert
- }
-}
-example.org {
- proxy . 8.8.8.8:53
-}
-example.com {
- proxy . 8.8.8.8:53
-}
-~~~
-
-## AutoPath
-
-The *kubernetes* middleware can be used in conjunction with the *autopath* middleware. Using this
-feature enables server-side domain search path completion in kubernetes clusters. Note: `pods` must
-be set to `verified` for this to function properly.
-
- cluster.local {
- autopath @kubernetes
- kubernetes {
- pods verified
- }
- }
-
-## Federation
-
-The *kubernetes* middleware can be used in conjunction with the *federation* middleware. Using this
-feature enables serving federated domains from the kubernetes clusters.
-
- cluster.local {
- federation {
- fallthrough
- prod prod.example.org
- staging staging.example.org
-
- }
- kubernetes
- }
-
-
-## Wildcards
-
-Some query labels accept a wildcard value to match any value. If a label is a valid wildcard (\*,
-or the word "any"), then that label will match all values. The labels that accept wildcards are:
-
- * _service_ in an `A` record request: _service_.namespace.svc.zone.
- * e.g. `*.ns.svc.myzone.local`
- * _namespace_ in an `A` record request: service._namespace_.svc.zone.
- * e.g. `nginx.*.svc.myzone.local`
- * _port and/or protocol_ in an `SRV` request: __port_.__protocol_.service.namespace.svc.zone.
- * e.g. `_http.*.service.ns.svc.`
- * multiple wild cards are allowed in a single query.
- * e.g. `A` Request `*.*.svc.zone.` or `SRV` request `*.*.*.*.svc.zone.`
diff --git a/middleware/kubernetes/apiproxy.go b/middleware/kubernetes/apiproxy.go
deleted file mode 100644
index f6d2c00fa..000000000
--- a/middleware/kubernetes/apiproxy.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package kubernetes
-
-import (
- "fmt"
- "io"
- "log"
- "net"
- "net/http"
-
- "github.com/coredns/coredns/middleware/pkg/healthcheck"
-)
-
-type proxyHandler struct {
- healthcheck.HealthCheck
-}
-
-type apiProxy struct {
- http.Server
- listener net.Listener
- handler proxyHandler
-}
-
-func (p *proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- upstream := p.Select()
- network := "tcp"
- if upstream.Network != "" {
- network = upstream.Network
- }
- address := upstream.Name
- d, err := net.Dial(network, address)
- if err != nil {
- log.Printf("[ERROR] Unable to establish connection to upstream %s://%s: %s", network, address, err)
- http.Error(w, fmt.Sprintf("Unable to establish connection to upstream %s://%s: %s", network, address, err), 500)
- return
- }
- hj, ok := w.(http.Hijacker)
- if !ok {
- log.Printf("[ERROR] Unable to establish connection: no hijacker")
- http.Error(w, "Unable to establish connection: no hijacker", 500)
- return
- }
- nc, _, err := hj.Hijack()
- if err != nil {
- log.Printf("[ERROR] Unable to hijack connection: %s", err)
- http.Error(w, fmt.Sprintf("Unable to hijack connection: %s", err), 500)
- return
- }
- defer nc.Close()
- defer d.Close()
-
- err = r.Write(d)
- if err != nil {
- log.Printf("[ERROR] Unable to copy connection to upstream %s://%s: %s", network, address, err)
- http.Error(w, fmt.Sprintf("Unable to copy connection to upstream %s://%s: %s", network, address, err), 500)
- return
- }
-
- errChan := make(chan error, 2)
- cp := func(dst io.Writer, src io.Reader) {
- _, err := io.Copy(dst, src)
- errChan <- err
- }
- go cp(d, nc)
- go cp(nc, d)
- <-errChan
-}
-
-func (p *apiProxy) Run() {
- p.handler.Start()
- p.Serve(p.listener)
-}
-
-func (p *apiProxy) Stop() {
- p.handler.Stop()
- p.listener.Close()
-}
diff --git a/middleware/kubernetes/autopath.go b/middleware/kubernetes/autopath.go
deleted file mode 100644
index be71ce326..000000000
--- a/middleware/kubernetes/autopath.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package kubernetes
-
-import (
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/request"
-
- "k8s.io/client-go/1.5/pkg/api"
-)
-
-// AutoPath implements the AutoPathFunc call from the autopath middleware.
-// It returns a per-query search path or nil indicating no searchpathing should happen.
-func (k *Kubernetes) AutoPath(state request.Request) []string {
- // Check if the query falls in a zone we are actually authoriative for and thus if we want autopath.
- zone := middleware.Zones(k.Zones).Matches(state.Name())
- if zone == "" {
- return nil
- }
-
- ip := state.IP()
-
- pod := k.podWithIP(ip)
- if pod == nil {
- return nil
- }
-
- search := make([]string, 3)
- if zone == "." {
- search[0] = pod.Namespace + ".svc."
- search[1] = "svc."
- search[2] = "."
- } else {
- search[0] = pod.Namespace + ".svc." + zone
- search[1] = "svc." + zone
- search[2] = zone
- }
-
- search = append(search, k.autoPathSearch...)
- search = append(search, "") // sentinal
- return search
-}
-
-// podWithIP return the api.Pod for source IP ip. It returns nil if nothing can be found.
-func (k *Kubernetes) podWithIP(ip string) (p *api.Pod) {
- objList := k.APIConn.PodIndex(ip)
- for _, o := range objList {
- p, ok := o.(*api.Pod)
- if !ok {
- return nil
- }
- return p
- }
- return nil
-}
diff --git a/middleware/kubernetes/controller.go b/middleware/kubernetes/controller.go
deleted file mode 100644
index b809264e1..000000000
--- a/middleware/kubernetes/controller.go
+++ /dev/null
@@ -1,399 +0,0 @@
-package kubernetes
-
-import (
- "errors"
- "fmt"
- "log"
- "sync"
- "time"
-
- "k8s.io/client-go/1.5/kubernetes"
- "k8s.io/client-go/1.5/pkg/api"
- unversionedapi "k8s.io/client-go/1.5/pkg/api/unversioned"
- "k8s.io/client-go/1.5/pkg/api/v1"
- "k8s.io/client-go/1.5/pkg/labels"
- "k8s.io/client-go/1.5/pkg/runtime"
- "k8s.io/client-go/1.5/pkg/watch"
- "k8s.io/client-go/1.5/tools/cache"
-)
-
-var (
- namespace = api.NamespaceAll
-)
-
-// storeToNamespaceLister makes a Store that lists Namespaces.
-type storeToNamespaceLister struct {
- cache.Store
-}
-
-const podIPIndex = "PodIP"
-
-// List lists all Namespaces in the store.
-func (s *storeToNamespaceLister) List() (ns api.NamespaceList, err error) {
- for _, m := range s.Store.List() {
- ns.Items = append(ns.Items, *(m.(*api.Namespace)))
- }
- return ns, nil
-}
-
-type dnsController interface {
- ServiceList() []*api.Service
- PodIndex(string) []interface{}
- EndpointsList() api.EndpointsList
-
- GetNodeByName(string) (api.Node, error)
-
- Run()
- Stop() error
-}
-
-type dnsControl struct {
- client *kubernetes.Clientset
-
- selector *labels.Selector
-
- svcController *cache.Controller
- podController *cache.Controller
- nsController *cache.Controller
- epController *cache.Controller
-
- svcLister cache.StoreToServiceLister
- podLister cache.StoreToPodLister
- nsLister storeToNamespaceLister
- epLister cache.StoreToEndpointsLister
-
- // stopLock is used to enforce only a single call to Stop is active.
- // Needed because we allow stopping through an http endpoint and
- // allowing concurrent stoppers leads to stack traces.
- stopLock sync.Mutex
- shutdown bool
- stopCh chan struct{}
-}
-
-type dnsControlOpts struct {
- initPodCache bool
- resyncPeriod time.Duration
- // Label handling.
- labelSelector *unversionedapi.LabelSelector
- selector *labels.Selector
-}
-
-// newDNSController creates a controller for CoreDNS.
-func newdnsController(kubeClient *kubernetes.Clientset, opts dnsControlOpts) *dnsControl {
- dns := dnsControl{
- client: kubeClient,
- selector: opts.selector,
- stopCh: make(chan struct{}),
- }
-
- dns.svcLister.Indexer, dns.svcController = cache.NewIndexerInformer(
- &cache.ListWatch{
- ListFunc: serviceListFunc(dns.client, namespace, dns.selector),
- WatchFunc: serviceWatchFunc(dns.client, namespace, dns.selector),
- },
- &api.Service{},
- opts.resyncPeriod,
- cache.ResourceEventHandlerFuncs{},
- cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
-
- if opts.initPodCache {
- dns.podLister.Indexer, dns.podController = cache.NewIndexerInformer(
- &cache.ListWatch{
- ListFunc: podListFunc(dns.client, namespace, dns.selector),
- WatchFunc: podWatchFunc(dns.client, namespace, dns.selector),
- },
- &api.Pod{}, // TODO replace with a lighter-weight custom struct
- opts.resyncPeriod,
- cache.ResourceEventHandlerFuncs{},
- cache.Indexers{podIPIndex: podIPIndexFunc})
- }
-
- dns.nsLister.Store, dns.nsController = cache.NewInformer(
- &cache.ListWatch{
- ListFunc: namespaceListFunc(dns.client, dns.selector),
- WatchFunc: namespaceWatchFunc(dns.client, dns.selector),
- },
- &api.Namespace{},
- opts.resyncPeriod,
- cache.ResourceEventHandlerFuncs{})
-
- dns.epLister.Store, dns.epController = cache.NewInformer(
- &cache.ListWatch{
- ListFunc: endpointsListFunc(dns.client, namespace, dns.selector),
- WatchFunc: endpointsWatchFunc(dns.client, namespace, dns.selector),
- },
- &api.Endpoints{},
- opts.resyncPeriod,
- cache.ResourceEventHandlerFuncs{})
-
- return &dns
-}
-
-func podIPIndexFunc(obj interface{}) ([]string, error) {
- p, ok := obj.(*api.Pod)
- if !ok {
- return nil, errors.New("obj was not an *api.Pod")
- }
- return []string{p.Status.PodIP}, nil
-}
-
-func serviceListFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(api.ListOptions) (runtime.Object, error) {
- return func(opts api.ListOptions) (runtime.Object, error) {
- if s != nil {
- opts.LabelSelector = *s
- }
- listV1, err := c.Core().Services(ns).List(opts)
-
- if err != nil {
- return nil, err
- }
- var listAPI api.ServiceList
- err = v1.Convert_v1_ServiceList_To_api_ServiceList(listV1, &listAPI, nil)
- if err != nil {
- return nil, err
- }
- return &listAPI, err
- }
-}
-
-func podListFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(api.ListOptions) (runtime.Object, error) {
- return func(opts api.ListOptions) (runtime.Object, error) {
- if s != nil {
- opts.LabelSelector = *s
- }
- listV1, err := c.Core().Pods(ns).List(opts)
-
- if err != nil {
- return nil, err
- }
- var listAPI api.PodList
- err = v1.Convert_v1_PodList_To_api_PodList(listV1, &listAPI, nil)
- if err != nil {
- return nil, err
- }
-
- return &listAPI, err
- }
-}
-
-func v1ToAPIFilter(in watch.Event) (out watch.Event, keep bool) {
- if in.Type == watch.Error {
- return in, true
- }
-
- switch v1Obj := in.Object.(type) {
- case *v1.Service:
- var apiObj api.Service
- err := v1.Convert_v1_Service_To_api_Service(v1Obj, &apiObj, nil)
- if err != nil {
- log.Printf("[ERROR] Could not convert v1.Service: %s", err)
- return in, true
- }
- return watch.Event{Type: in.Type, Object: &apiObj}, true
- case *v1.Pod:
- var apiObj api.Pod
- err := v1.Convert_v1_Pod_To_api_Pod(v1Obj, &apiObj, nil)
- if err != nil {
- log.Printf("[ERROR] Could not convert v1.Pod: %s", err)
- return in, true
- }
- return watch.Event{Type: in.Type, Object: &apiObj}, true
- case *v1.Namespace:
- var apiObj api.Namespace
- err := v1.Convert_v1_Namespace_To_api_Namespace(v1Obj, &apiObj, nil)
- if err != nil {
- log.Printf("[ERROR] Could not convert v1.Namespace: %s", err)
- return in, true
- }
- return watch.Event{Type: in.Type, Object: &apiObj}, true
- case *v1.Endpoints:
- var apiObj api.Endpoints
- err := v1.Convert_v1_Endpoints_To_api_Endpoints(v1Obj, &apiObj, nil)
- if err != nil {
- log.Printf("[ERROR] Could not convert v1.Endpoint: %s", err)
- return in, true
- }
- return watch.Event{Type: in.Type, Object: &apiObj}, true
- }
-
- log.Printf("[WARN] Unhandled v1 type in event: %v", in)
- return in, true
-}
-
-func serviceWatchFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(options api.ListOptions) (watch.Interface, error) {
- return func(options api.ListOptions) (watch.Interface, error) {
- if s != nil {
- options.LabelSelector = *s
- }
- w, err := c.Core().Services(ns).Watch(options)
- if err != nil {
- return nil, err
- }
- return watch.Filter(w, v1ToAPIFilter), nil
- }
-}
-
-func podWatchFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(options api.ListOptions) (watch.Interface, error) {
- return func(options api.ListOptions) (watch.Interface, error) {
- if s != nil {
- options.LabelSelector = *s
- }
- w, err := c.Core().Pods(ns).Watch(options)
-
- if err != nil {
- return nil, err
- }
- return watch.Filter(w, v1ToAPIFilter), nil
- }
-}
-
-func namespaceListFunc(c *kubernetes.Clientset, s *labels.Selector) func(api.ListOptions) (runtime.Object, error) {
- return func(opts api.ListOptions) (runtime.Object, error) {
- if s != nil {
- opts.LabelSelector = *s
- }
- listV1, err := c.Core().Namespaces().List(opts)
- if err != nil {
- return nil, err
- }
- var listAPI api.NamespaceList
- err = v1.Convert_v1_NamespaceList_To_api_NamespaceList(listV1, &listAPI, nil)
- if err != nil {
- return nil, err
- }
- return &listAPI, err
- }
-}
-
-func namespaceWatchFunc(c *kubernetes.Clientset, s *labels.Selector) func(options api.ListOptions) (watch.Interface, error) {
- return func(options api.ListOptions) (watch.Interface, error) {
- if s != nil {
- options.LabelSelector = *s
- }
- w, err := c.Core().Namespaces().Watch(options)
- if err != nil {
- return nil, err
- }
- return watch.Filter(w, v1ToAPIFilter), nil
- }
-}
-
-func endpointsListFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(api.ListOptions) (runtime.Object, error) {
- return func(opts api.ListOptions) (runtime.Object, error) {
- if s != nil {
- opts.LabelSelector = *s
- }
- listV1, err := c.Core().Endpoints(ns).List(opts)
-
- if err != nil {
- return nil, err
- }
- var listAPI api.EndpointsList
- err = v1.Convert_v1_EndpointsList_To_api_EndpointsList(listV1, &listAPI, nil)
- if err != nil {
- return nil, err
- }
- return &listAPI, err
- }
-}
-
-func endpointsWatchFunc(c *kubernetes.Clientset, ns string, s *labels.Selector) func(options api.ListOptions) (watch.Interface, error) {
- return func(options api.ListOptions) (watch.Interface, error) {
- if s != nil {
- options.LabelSelector = *s
- }
- w, err := c.Core().Endpoints(ns).Watch(options)
- if err != nil {
- return nil, err
- }
- return watch.Filter(w, v1ToAPIFilter), nil
- }
-}
-
-func (dns *dnsControl) controllersInSync() bool {
- hs := dns.svcController.HasSynced() &&
- dns.nsController.HasSynced() &&
- dns.epController.HasSynced()
-
- if dns.podController != nil {
- hs = hs && dns.podController.HasSynced()
- }
-
- return hs
-}
-
-// Stop stops the controller.
-func (dns *dnsControl) Stop() error {
- dns.stopLock.Lock()
- defer dns.stopLock.Unlock()
-
- // Only try draining the workqueue if we haven't already.
- if !dns.shutdown {
- close(dns.stopCh)
- dns.shutdown = true
-
- return nil
- }
-
- return fmt.Errorf("shutdown already in progress")
-}
-
-// Run starts the controller.
-func (dns *dnsControl) Run() {
- go dns.svcController.Run(dns.stopCh)
- go dns.nsController.Run(dns.stopCh)
- go dns.epController.Run(dns.stopCh)
- if dns.podController != nil {
- go dns.podController.Run(dns.stopCh)
- }
- <-dns.stopCh
-}
-
-func (dns *dnsControl) NamespaceList() *api.NamespaceList {
- nsList, err := dns.nsLister.List()
- if err != nil {
- return &api.NamespaceList{}
- }
-
- return &nsList
-}
-
-func (dns *dnsControl) ServiceList() []*api.Service {
- svcs, err := dns.svcLister.List(labels.Everything())
- if err != nil {
- return []*api.Service{}
- }
-
- return svcs
-}
-
-func (dns *dnsControl) PodIndex(ip string) []interface{} {
- pods, err := dns.podLister.Indexer.ByIndex(podIPIndex, ip)
- if err != nil {
- return nil
- }
-
- return pods
-}
-
-func (dns *dnsControl) EndpointsList() api.EndpointsList {
- epl, err := dns.epLister.List()
- if err != nil {
- return api.EndpointsList{}
- }
-
- return epl
-}
-
-func (dns *dnsControl) GetNodeByName(name string) (api.Node, error) {
- v1node, err := dns.client.Core().Nodes().Get(name)
- if err != nil {
- return api.Node{}, err
- }
- var apinode api.Node
- err = v1.Convert_v1_Node_To_api_Node(v1node, &apinode, nil)
- if err != nil {
- return api.Node{}, err
- }
- return apinode, nil
-}
diff --git a/middleware/kubernetes/federation.go b/middleware/kubernetes/federation.go
deleted file mode 100644
index f192a4a76..000000000
--- a/middleware/kubernetes/federation.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package kubernetes
-
-import (
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/request"
-)
-
-// The federation node.Labels keys used.
-const (
- // TODO: Do not hardcode these labels. Pull them out of the API instead.
- //
- // We can get them via ....
- // import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- // metav1.LabelZoneFailureDomain
- // metav1.LabelZoneRegion
- //
- // But importing above breaks coredns with flag collision of 'log_dir'
-
- LabelZone = "failure-domain.beta.kubernetes.io/zone"
- LabelRegion = "failure-domain.beta.kubernetes.io/region"
-)
-
-// Federations is used from the federations middleware to return the service that should be
-// returned as a CNAME for federation(s) to work.
-func (k *Kubernetes) Federations(state request.Request, fname, fzone string) (msg.Service, error) {
- nodeName := k.localNodeName()
- node, err := k.APIConn.GetNodeByName(nodeName)
- if err != nil {
- return msg.Service{}, err
- }
- r, err := parseRequest(state)
- if err != nil {
- return msg.Service{}, err
- }
-
- lz := node.Labels[LabelZone]
- lr := node.Labels[LabelRegion]
-
- if r.endpoint == "" {
- return msg.Service{Host: dnsutil.Join([]string{r.service, r.namespace, fname, r.podOrSvc, lz, lr, fzone})}, nil
- }
-
- return msg.Service{Host: dnsutil.Join([]string{r.endpoint, r.service, r.namespace, fname, r.podOrSvc, lz, lr, fzone})}, nil
-}
diff --git a/middleware/kubernetes/handler.go b/middleware/kubernetes/handler.go
deleted file mode 100644
index e83ab3d7d..000000000
--- a/middleware/kubernetes/handler.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package kubernetes
-
-import (
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// ServeDNS implements the middleware.Handler interface.
-func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- state := request.Request{W: w, Req: r}
-
- m := new(dns.Msg)
- m.SetReply(r)
- m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
-
- zone := middleware.Zones(k.Zones).Matches(state.Name())
- if zone == "" {
- return middleware.NextOrFailure(k.Name(), k.Next, ctx, w, r)
- }
-
- state.Zone = zone
-
- var (
- records []dns.RR
- extra []dns.RR
- err error
- )
-
- switch state.Type() {
- case "A":
- records, err = middleware.A(&k, zone, state, nil, middleware.Options{})
- case "AAAA":
- records, err = middleware.AAAA(&k, zone, state, nil, middleware.Options{})
- case "TXT":
- records, err = middleware.TXT(&k, zone, state, middleware.Options{})
- case "CNAME":
- records, err = middleware.CNAME(&k, zone, state, middleware.Options{})
- case "PTR":
- records, err = middleware.PTR(&k, zone, state, middleware.Options{})
- case "MX":
- records, extra, err = middleware.MX(&k, zone, state, middleware.Options{})
- case "SRV":
- records, extra, err = middleware.SRV(&k, zone, state, middleware.Options{})
- case "SOA":
- records, err = middleware.SOA(&k, zone, state, middleware.Options{})
- case "NS":
- if state.Name() == zone {
- records, extra, err = middleware.NS(&k, zone, state, middleware.Options{})
- break
- }
- fallthrough
- default:
- // Do a fake A lookup, so we can distinguish between NODATA and NXDOMAIN
- _, err = middleware.A(&k, zone, state, nil, middleware.Options{})
- }
-
- if k.IsNameError(err) {
- if k.Fallthrough {
- return middleware.NextOrFailure(k.Name(), k.Next, ctx, w, r)
- }
- return middleware.BackendError(&k, zone, dns.RcodeNameError, state, nil /* err */, middleware.Options{})
- }
- if err != nil {
- return dns.RcodeServerFailure, err
- }
-
- if len(records) == 0 {
- return middleware.BackendError(&k, zone, dns.RcodeSuccess, state, nil, middleware.Options{})
- }
-
- m.Answer = append(m.Answer, records...)
- m.Extra = append(m.Extra, extra...)
-
- m = dnsutil.Dedup(m)
- state.SizeAndDo(m)
- m, _ = state.Scrub(m)
- w.WriteMsg(m)
- return dns.RcodeSuccess, nil
-}
-
-// Name implements the Handler interface.
-func (k Kubernetes) Name() string { return "kubernetes" }
diff --git a/middleware/kubernetes/handler_pod_disabled_test.go b/middleware/kubernetes/handler_pod_disabled_test.go
deleted file mode 100644
index b1fb7604c..000000000
--- a/middleware/kubernetes/handler_pod_disabled_test.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package kubernetes
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-var podModeDisabledCases = []test.Case{
- {
- Qname: "10-240-0-1.podns.pod.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeNameError,
- Error: errPodsDisabled,
- Ns: []dns.RR{
- test.SOA("cluster.local. 300 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 60"),
- },
- },
- {
- Qname: "172-0-0-2.podns.pod.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeNameError,
- Error: errPodsDisabled,
- Ns: []dns.RR{
- test.SOA("cluster.local. 300 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 60"),
- },
- },
-}
-
-func TestServeDNSModeDisabled(t *testing.T) {
-
- k := New([]string{"cluster.local."})
- k.APIConn = &APIConnServeTest{}
- k.Next = test.NextHandler(dns.RcodeSuccess, nil)
- k.podMode = podModeDisabled
- ctx := context.TODO()
-
- for i, tc := range podModeDisabledCases {
- r := tc.Msg()
-
- w := dnsrecorder.New(&test.ResponseWriter{})
-
- _, err := k.ServeDNS(ctx, w, r)
- if err != tc.Error {
- t.Errorf("Test %d expected no error, got %v", i, err)
- return
- }
- if tc.Error != nil {
- continue
- }
-
- resp := w.Msg
- if resp == nil {
- t.Fatalf("Test %d, got nil message and no error for %q", i, r.Question[0].Name)
- }
-
- test.SortAndCheck(t, resp, tc)
- }
-}
diff --git a/middleware/kubernetes/handler_pod_insecure_test.go b/middleware/kubernetes/handler_pod_insecure_test.go
deleted file mode 100644
index bc09f3c4f..000000000
--- a/middleware/kubernetes/handler_pod_insecure_test.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package kubernetes
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-var podModeInsecureCases = []test.Case{
- {
- Qname: "10-240-0-1.podns.pod.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.A("10-240-0-1.podns.pod.cluster.local. 0 IN A 10.240.0.1"),
- },
- },
- {
- Qname: "172-0-0-2.podns.pod.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.A("172-0-0-2.podns.pod.cluster.local. 0 IN A 172.0.0.2"),
- },
- },
-}
-
-func TestServeDNSModeInsecure(t *testing.T) {
-
- k := New([]string{"cluster.local."})
- k.APIConn = &APIConnServeTest{}
- k.Next = test.NextHandler(dns.RcodeSuccess, nil)
- ctx := context.TODO()
- k.podMode = podModeInsecure
-
- for i, tc := range podModeInsecureCases {
- r := tc.Msg()
-
- w := dnsrecorder.New(&test.ResponseWriter{})
-
- _, err := k.ServeDNS(ctx, w, r)
- if err != tc.Error {
- t.Errorf("Test %d expected no error, got %v", i, err)
- return
- }
- if tc.Error != nil {
- continue
- }
-
- resp := w.Msg
- if resp == nil {
- t.Fatalf("Test %d, got nil message and no error for %q", i, r.Question[0].Name)
- }
-
- test.SortAndCheck(t, resp, tc)
- }
-}
diff --git a/middleware/kubernetes/handler_pod_verified_test.go b/middleware/kubernetes/handler_pod_verified_test.go
deleted file mode 100644
index 879e785af..000000000
--- a/middleware/kubernetes/handler_pod_verified_test.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package kubernetes
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-var podModeVerifiedCases = []test.Case{
- {
- Qname: "10-240-0-1.podns.pod.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.A("10-240-0-1.podns.pod.cluster.local. 0 IN A 10.240.0.1"),
- },
- },
- {
- Qname: "172-0-0-2.podns.pod.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeNameError,
- Ns: []dns.RR{
- test.SOA("cluster.local. 300 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 60"),
- },
- },
-}
-
-func TestServeDNSModeVerified(t *testing.T) {
-
- k := New([]string{"cluster.local."})
- k.APIConn = &APIConnServeTest{}
- k.Next = test.NextHandler(dns.RcodeSuccess, nil)
- ctx := context.TODO()
- k.podMode = podModeVerified
-
- for i, tc := range podModeVerifiedCases {
- r := tc.Msg()
-
- w := dnsrecorder.New(&test.ResponseWriter{})
-
- _, err := k.ServeDNS(ctx, w, r)
- if err != tc.Error {
- t.Errorf("Test %d expected no error, got %v", i, err)
- return
- }
- if tc.Error != nil {
- continue
- }
-
- resp := w.Msg
- if resp == nil {
- t.Fatalf("Test %d, got nil message and no error for %q", i, r.Question[0].Name)
- }
-
- test.SortAndCheck(t, resp, tc)
- }
-}
diff --git a/middleware/kubernetes/handler_test.go b/middleware/kubernetes/handler_test.go
deleted file mode 100644
index 8ab51aff8..000000000
--- a/middleware/kubernetes/handler_test.go
+++ /dev/null
@@ -1,347 +0,0 @@
-package kubernetes
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
- "k8s.io/client-go/1.5/pkg/api"
-)
-
-var dnsTestCases = []test.Case{
- // A Service
- {
- Qname: "svc1.testns.svc.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.A("svc1.testns.svc.cluster.local. 5 IN A 10.0.0.1"),
- },
- },
- // A Service (wildcard)
- {
- Qname: "svc1.*.svc.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.A("svc1.*.svc.cluster.local. 5 IN A 10.0.0.1"),
- },
- },
- {
- Qname: "svc1.testns.svc.cluster.local.", Qtype: dns.TypeSRV,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{test.SRV("svc1.testns.svc.cluster.local. 303 IN SRV 0 100 80 svc1.testns.svc.cluster.local.")},
- Extra: []dns.RR{test.A("svc1.testns.svc.cluster.local. 303 IN A 10.0.0.1")},
- },
- // SRV Service (wildcard)
- {
- Qname: "svc1.*.svc.cluster.local.", Qtype: dns.TypeSRV,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{test.SRV("svc1.*.svc.cluster.local. 303 IN SRV 0 100 80 svc1.testns.svc.cluster.local.")},
- Extra: []dns.RR{test.A("svc1.testns.svc.cluster.local. 303 IN A 10.0.0.1")},
- },
- // SRV Service (wildcards)
- {
- Qname: "*.any.svc1.*.svc.cluster.local.", Qtype: dns.TypeSRV,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{test.SRV("*.any.svc1.*.svc.cluster.local. 303 IN SRV 0 100 80 svc1.testns.svc.cluster.local.")},
- Extra: []dns.RR{test.A("svc1.testns.svc.cluster.local. 303 IN A 10.0.0.1")},
- },
- // A Service (wildcards)
- {
- Qname: "*.any.svc1.*.svc.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.A("*.any.svc1.*.svc.cluster.local. 303 IN A 10.0.0.1"),
- },
- },
- // SRV Service Not udp/tcp
- {
- Qname: "*._not-udp-or-tcp.svc1.testns.svc.cluster.local.", Qtype: dns.TypeSRV,
- Rcode: dns.RcodeNameError,
- Ns: []dns.RR{
- test.SOA("cluster.local. 300 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 60"),
- },
- },
- // SRV Service
- {
- Qname: "_http._tcp.svc1.testns.svc.cluster.local.", Qtype: dns.TypeSRV,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.SRV("_http._tcp.svc1.testns.svc.cluster.local. 303 IN SRV 0 100 80 svc1.testns.svc.cluster.local."),
- },
- Extra: []dns.RR{
- test.A("svc1.testns.svc.cluster.local. 303 IN A 10.0.0.1"),
- },
- },
- // A Service (Headless)
- {
- Qname: "hdls1.testns.svc.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.A("hdls1.testns.svc.cluster.local. 303 IN A 172.0.0.2"),
- test.A("hdls1.testns.svc.cluster.local. 303 IN A 172.0.0.3"),
- },
- },
- // SRV Service (Headless)
- {
- Qname: "_http._tcp.hdls1.testns.svc.cluster.local.", Qtype: dns.TypeSRV,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.SRV("_http._tcp.hdls1.testns.svc.cluster.local. 303 IN SRV 0 50 80 172-0-0-2.hdls1.testns.svc.cluster.local."),
- test.SRV("_http._tcp.hdls1.testns.svc.cluster.local. 303 IN SRV 0 50 80 172-0-0-3.hdls1.testns.svc.cluster.local."),
- },
- Extra: []dns.RR{
- test.A("172-0-0-2.hdls1.testns.svc.cluster.local. 303 IN A 172.0.0.2"),
- test.A("172-0-0-3.hdls1.testns.svc.cluster.local. 303 IN A 172.0.0.3"),
- },
- },
- // CNAME External
- {
- Qname: "external.testns.svc.cluster.local.", Qtype: dns.TypeCNAME,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.CNAME("external.testns.svc.cluster.local. 303 IN CNAME ext.interwebs.test."),
- },
- },
- // AAAA Service (existing service)
- {
- Qname: "svc1.testns.svc.cluster.local.", Qtype: dns.TypeAAAA,
- Rcode: dns.RcodeSuccess,
- Ns: []dns.RR{
- test.SOA("cluster.local. 300 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 60"),
- },
- },
- // AAAA Service (non-existing service)
- {
- Qname: "svc0.testns.svc.cluster.local.", Qtype: dns.TypeAAAA,
- Rcode: dns.RcodeNameError,
- Ns: []dns.RR{
- test.SOA("cluster.local. 300 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 60"),
- },
- },
- // A Service (non-existing service)
- {
- Qname: "svc0.testns.svc.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeNameError,
- Ns: []dns.RR{
- test.SOA("cluster.local. 300 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 60"),
- },
- },
- // TXT Schema
- {
- Qname: "dns-version.cluster.local.", Qtype: dns.TypeTXT,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.TXT("dns-version.cluster.local 28800 IN TXT 1.0.1"),
- },
- },
- // A Service (Headless) does not exist
- {
- Qname: "bogusendpoint.hdls1.testns.svc.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeNameError,
- Ns: []dns.RR{
- test.SOA("cluster.local. 300 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 60"),
- },
- },
- // A Service does not exist
- {
- Qname: "bogusendpoint.svc0.testns.svc.cluster.local.", Qtype: dns.TypeA,
- Rcode: dns.RcodeNameError,
- Ns: []dns.RR{
- test.SOA("cluster.local. 300 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 60"),
- },
- },
-}
-
-func TestServeDNS(t *testing.T) {
-
- k := New([]string{"cluster.local."})
- k.APIConn = &APIConnServeTest{}
- k.Next = test.NextHandler(dns.RcodeSuccess, nil)
- ctx := context.TODO()
-
- for i, tc := range dnsTestCases {
- r := tc.Msg()
-
- w := dnsrecorder.New(&test.ResponseWriter{})
-
- _, err := k.ServeDNS(ctx, w, r)
- if err != tc.Error {
- t.Errorf("Test %d expected no error, got %v", i, err)
- return
- }
- if tc.Error != nil {
- continue
- }
-
- resp := w.Msg
- if resp == nil {
- t.Fatalf("Test %d, got nil message and no error for %q", i, r.Question[0].Name)
- }
-
- // Before sorting, make sure that CNAMES do not appear after their target records
- test.CNAMEOrder(t, resp)
-
- test.SortAndCheck(t, resp, tc)
- }
-}
-
-type APIConnServeTest struct{}
-
-func (APIConnServeTest) Run() { return }
-func (APIConnServeTest) Stop() error { return nil }
-
-func (APIConnServeTest) PodIndex(string) []interface{} {
- a := make([]interface{}, 1)
- a[0] = &api.Pod{
- ObjectMeta: api.ObjectMeta{
- Namespace: "podns",
- },
- Status: api.PodStatus{
- PodIP: "10.240.0.1", // Remote IP set in test.ResponseWriter
- },
- }
- return a
-}
-
-func (APIConnServeTest) ServiceList() []*api.Service {
- svcs := []*api.Service{
- {
- ObjectMeta: api.ObjectMeta{
- Name: "svc1",
- Namespace: "testns",
- },
- Spec: api.ServiceSpec{
- ClusterIP: "10.0.0.1",
- Ports: []api.ServicePort{{
- Name: "http",
- Protocol: "tcp",
- Port: 80,
- }},
- },
- },
- {
- ObjectMeta: api.ObjectMeta{
- Name: "hdls1",
- Namespace: "testns",
- },
- Spec: api.ServiceSpec{
- ClusterIP: api.ClusterIPNone,
- },
- },
- {
- ObjectMeta: api.ObjectMeta{
- Name: "external",
- Namespace: "testns",
- },
- Spec: api.ServiceSpec{
- ExternalName: "ext.interwebs.test",
- Ports: []api.ServicePort{{
- Name: "http",
- Protocol: "tcp",
- Port: 80,
- }},
- },
- },
- }
- return svcs
-
-}
-
-func (APIConnServeTest) EndpointsList() api.EndpointsList {
- n := "test.node.foo.bar"
-
- return api.EndpointsList{
- Items: []api.Endpoints{
- {
- Subsets: []api.EndpointSubset{
- {
- Addresses: []api.EndpointAddress{
- {
- IP: "172.0.0.1",
- Hostname: "ep1a",
- },
- },
- Ports: []api.EndpointPort{
- {
- Port: 80,
- Protocol: "tcp",
- Name: "http",
- },
- },
- },
- },
- ObjectMeta: api.ObjectMeta{
- Name: "svc1",
- Namespace: "testns",
- },
- },
- {
- Subsets: []api.EndpointSubset{
- {
- Addresses: []api.EndpointAddress{
- {
- IP: "172.0.0.2",
- },
- },
- Ports: []api.EndpointPort{
- {
- Port: 80,
- Protocol: "tcp",
- Name: "http",
- },
- },
- },
- },
- ObjectMeta: api.ObjectMeta{
- Name: "hdls1",
- Namespace: "testns",
- },
- },
- {
- Subsets: []api.EndpointSubset{
- {
- Addresses: []api.EndpointAddress{
- {
- IP: "172.0.0.3",
- },
- },
- Ports: []api.EndpointPort{
- {
- Port: 80,
- Protocol: "tcp",
- Name: "http",
- },
- },
- },
- },
- ObjectMeta: api.ObjectMeta{
- Name: "hdls1",
- Namespace: "testns",
- },
- },
- {
- Subsets: []api.EndpointSubset{
- {
- Addresses: []api.EndpointAddress{
- {
- IP: "10.9.8.7",
- NodeName: &n,
- },
- },
- },
- },
- },
- },
- }
-}
-
-func (APIConnServeTest) GetNodeByName(name string) (api.Node, error) {
- return api.Node{
- ObjectMeta: api.ObjectMeta{
- Name: "test.node.foo.bar",
- },
- }, nil
-}
diff --git a/middleware/kubernetes/kubernetes.go b/middleware/kubernetes/kubernetes.go
deleted file mode 100644
index fe58df74d..000000000
--- a/middleware/kubernetes/kubernetes.go
+++ /dev/null
@@ -1,457 +0,0 @@
-// Package kubernetes provides the kubernetes backend.
-package kubernetes
-
-import (
- "errors"
- "fmt"
- "net"
- "strings"
- "sync/atomic"
- "time"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/middleware/pkg/healthcheck"
- "github.com/coredns/coredns/middleware/proxy"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "k8s.io/client-go/1.5/kubernetes"
- "k8s.io/client-go/1.5/pkg/api"
- unversionedapi "k8s.io/client-go/1.5/pkg/api/unversioned"
- "k8s.io/client-go/1.5/pkg/labels"
- "k8s.io/client-go/1.5/rest"
- "k8s.io/client-go/1.5/tools/clientcmd"
- clientcmdapi "k8s.io/client-go/1.5/tools/clientcmd/api"
-)
-
-// Kubernetes implements a middleware that connects to a Kubernetes cluster.
-type Kubernetes struct {
- Next middleware.Handler
- Zones []string
- Proxy proxy.Proxy // Proxy for looking up names during the resolution process
- APIServerList []string
- APIProxy *apiProxy
- APICertAuth string
- APIClientCert string
- APIClientKey string
- APIConn dnsController
- Namespaces map[string]bool
- podMode string
- Fallthrough bool
- ttl uint32
-
- primaryZoneIndex int
- interfaceAddrsFunc func() net.IP
- autoPathSearch []string // Local search path from /etc/resolv.conf. Needed for autopath.
-}
-
-// New returns a intialized Kubernetes. It default interfaceAddrFunc to return 127.0.0.1. All other
-// values default to their zero value, primaryZoneIndex will thus point to the first zone.
-func New(zones []string) *Kubernetes {
- k := new(Kubernetes)
- k.Zones = zones
- k.Namespaces = make(map[string]bool)
- k.interfaceAddrsFunc = func() net.IP { return net.ParseIP("127.0.0.1") }
- k.podMode = podModeDisabled
- k.Proxy = proxy.Proxy{}
- k.ttl = defaultTTL
-
- return k
-}
-
-const (
- // podModeDisabled is the default value where pod requests are ignored
- podModeDisabled = "disabled"
- // podModeVerified is where Pod requests are answered only if they exist
- podModeVerified = "verified"
- // podModeInsecure is where pod requests are answered without verfying they exist
- podModeInsecure = "insecure"
- // DNSSchemaVersion is the schema version: https://github.com/kubernetes/dns/blob/master/docs/specification.md
- DNSSchemaVersion = "1.0.1"
-)
-
-var (
- errNoItems = errors.New("no items found")
- errNsNotExposed = errors.New("namespace is not exposed")
- errInvalidRequest = errors.New("invalid query name")
- errAPIBadPodType = errors.New("expected type *api.Pod")
- errPodsDisabled = errors.New("pod records disabled")
-)
-
-// Services implements the ServiceBackend interface.
-func (k *Kubernetes) Services(state request.Request, exact bool, opt middleware.Options) (svcs []msg.Service, err error) {
-
- // We're looking again at types, which we've already done in ServeDNS, but there are some types k8s just can't answer.
- switch state.QType() {
-
- case dns.TypeTXT:
- // 1 label + zone, label must be "dns-version".
- t, _ := dnsutil.TrimZone(state.Name(), state.Zone)
-
- segs := dns.SplitDomainName(t)
- if len(segs) != 1 {
- return nil, fmt.Errorf("kubernetes: TXT query can only be for dns-version: %s", state.QName())
- }
- if segs[0] != "dns-version" {
- return nil, nil
- }
- svc := msg.Service{Text: DNSSchemaVersion, TTL: 28800, Key: msg.Path(state.QName(), "coredns")}
- return []msg.Service{svc}, nil
-
- case dns.TypeNS:
- // We can only get here if the qname equal the zone, see ServeDNS in handler.go.
- ns := k.nsAddr()
- svc := msg.Service{Host: ns.A.String(), Key: msg.Path(state.QName(), "coredns")}
- return []msg.Service{svc}, nil
- }
-
- if state.QType() == dns.TypeA && isDefaultNS(state.Name(), state.Zone) {
- // If this is an A request for "ns.dns", respond with a "fake" record for coredns.
- // SOA records always use this hardcoded name
- ns := k.nsAddr()
- svc := msg.Service{Host: ns.A.String(), Key: msg.Path(state.QName(), "coredns")}
- return []msg.Service{svc}, nil
- }
-
- s, e := k.Records(state, false)
-
- // SRV for external services is not yet implemented, so remove those records.
-
- if state.QType() != dns.TypeSRV {
- return s, e
- }
-
- internal := []msg.Service{}
- for _, svc := range s {
- if t, _ := svc.HostType(); t != dns.TypeCNAME {
- internal = append(internal, svc)
- }
- }
-
- return internal, e
-}
-
-// primaryZone will return the first non-reverse zone being handled by this middleware
-func (k *Kubernetes) primaryZone() string { return k.Zones[k.primaryZoneIndex] }
-
-// Lookup implements the ServiceBackend interface.
-func (k *Kubernetes) Lookup(state request.Request, name string, typ uint16) (*dns.Msg, error) {
- return k.Proxy.Lookup(state, name, typ)
-}
-
-// IsNameError implements the ServiceBackend interface.
-func (k *Kubernetes) IsNameError(err error) bool {
- return err == errNoItems || err == errNsNotExposed || err == errInvalidRequest
-}
-
-func (k *Kubernetes) getClientConfig() (*rest.Config, error) {
- loadingRules := &clientcmd.ClientConfigLoadingRules{}
- overrides := &clientcmd.ConfigOverrides{}
- clusterinfo := clientcmdapi.Cluster{}
- authinfo := clientcmdapi.AuthInfo{}
-
- if len(k.APIServerList) == 0 {
- cc, err := rest.InClusterConfig()
- if err != nil {
- return nil, err
- }
- return cc, err
- }
-
- endpoint := k.APIServerList[0]
- if len(k.APIServerList) > 1 {
- // Use a random port for api proxy, will get the value later through listener.Addr()
- listener, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- return nil, fmt.Errorf("failed to create kubernetes api proxy: %v", err)
- }
- k.APIProxy = &apiProxy{
- listener: listener,
- handler: proxyHandler{
- HealthCheck: healthcheck.HealthCheck{
- FailTimeout: 3 * time.Second,
- MaxFails: 1,
- Future: 10 * time.Second,
- Path: "/",
- Interval: 5 * time.Second,
- },
- },
- }
- k.APIProxy.handler.Hosts = make([]*healthcheck.UpstreamHost, len(k.APIServerList))
- for i, entry := range k.APIServerList {
-
- uh := &healthcheck.UpstreamHost{
- Name: strings.TrimPrefix(entry, "http://"),
-
- CheckDown: func(upstream *proxyHandler) healthcheck.UpstreamHostDownFunc {
- return func(uh *healthcheck.UpstreamHost) bool {
-
- down := false
-
- uh.CheckMu.Lock()
- until := uh.OkUntil
- uh.CheckMu.Unlock()
-
- if !until.IsZero() && time.Now().After(until) {
- down = true
- }
-
- fails := atomic.LoadInt32(&uh.Fails)
- if fails >= upstream.MaxFails && upstream.MaxFails != 0 {
- down = true
- }
- return down
- }
- }(&k.APIProxy.handler),
- }
-
- k.APIProxy.handler.Hosts[i] = uh
- }
- k.APIProxy.Handler = &k.APIProxy.handler
-
- // Find the random port used for api proxy
- endpoint = fmt.Sprintf("http://%s", listener.Addr())
- }
- clusterinfo.Server = endpoint
-
- if len(k.APICertAuth) > 0 {
- clusterinfo.CertificateAuthority = k.APICertAuth
- }
- if len(k.APIClientCert) > 0 {
- authinfo.ClientCertificate = k.APIClientCert
- }
- if len(k.APIClientKey) > 0 {
- authinfo.ClientKey = k.APIClientKey
- }
-
- overrides.ClusterInfo = clusterinfo
- overrides.AuthInfo = authinfo
- clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)
-
- return clientConfig.ClientConfig()
-}
-
-// initKubeCache initializes a new Kubernetes cache.
-func (k *Kubernetes) initKubeCache(opts dnsControlOpts) (err error) {
-
- config, err := k.getClientConfig()
- if err != nil {
- return err
- }
-
- kubeClient, err := kubernetes.NewForConfig(config)
- if err != nil {
- return fmt.Errorf("failed to create kubernetes notification controller: %q", err)
- }
-
- if opts.labelSelector != nil {
- var selector labels.Selector
- selector, err = unversionedapi.LabelSelectorAsSelector(opts.labelSelector)
- if err != nil {
- return fmt.Errorf("unable to create Selector for LabelSelector '%s': %q", opts.labelSelector, err)
- }
- opts.selector = &selector
- }
-
- opts.initPodCache = k.podMode == podModeVerified
-
- k.APIConn = newdnsController(kubeClient, opts)
-
- return err
-}
-
-// Records looks up services in kubernetes.
-func (k *Kubernetes) Records(state request.Request, exact bool) ([]msg.Service, error) {
- r, e := parseRequest(state)
- if e != nil {
- return nil, e
- }
-
- if !wildcard(r.namespace) && !k.namespaceExposed(r.namespace) {
- return nil, errNsNotExposed
- }
-
- if r.podOrSvc == Pod {
- pods, err := k.findPods(r, state.Zone)
- return pods, err
- }
-
- services, err := k.findServices(r, state.Zone)
- return services, err
-}
-
-func endpointHostname(addr api.EndpointAddress) string {
- if addr.Hostname != "" {
- return strings.ToLower(addr.Hostname)
- }
- if strings.Contains(addr.IP, ".") {
- return strings.Replace(addr.IP, ".", "-", -1)
- }
- if strings.Contains(addr.IP, ":") {
- return strings.ToLower(strings.Replace(addr.IP, ":", "-", -1))
- }
- return ""
-}
-
-func (k *Kubernetes) findPods(r recordRequest, zone string) (pods []msg.Service, err error) {
- if k.podMode == podModeDisabled {
- return nil, errPodsDisabled
- }
-
- namespace := r.namespace
- podname := r.service
- zonePath := msg.Path(zone, "coredns")
- ip := ""
- err = errNoItems
-
- if strings.Count(podname, "-") == 3 && !strings.Contains(podname, "--") {
- ip = strings.Replace(podname, "-", ".", -1)
- } else {
- ip = strings.Replace(podname, "-", ":", -1)
- }
-
- if k.podMode == podModeInsecure {
- return []msg.Service{{Key: strings.Join([]string{zonePath, Pod, namespace, podname}, "/"), Host: ip}}, nil
- }
-
- // PodModeVerified
- objList := k.APIConn.PodIndex(ip)
-
- for _, o := range objList {
- p, ok := o.(*api.Pod)
- if !ok {
- return nil, errAPIBadPodType
- }
- // If namespace has a wildcard, filter results against Corefile namespace list.
- if wildcard(namespace) && !k.namespaceExposed(p.Namespace) {
- continue
- }
- // check for matching ip and namespace
- if ip == p.Status.PodIP && match(namespace, p.Namespace) {
- s := msg.Service{Key: strings.Join([]string{zonePath, Pod, namespace, podname}, "/"), Host: ip}
- pods = append(pods, s)
-
- err = nil
- }
- }
- return pods, err
-}
-
-// findServices returns the services matching r from the cache.
-func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.Service, err error) {
- serviceList := k.APIConn.ServiceList()
- zonePath := msg.Path(zone, "coredns")
- err = errNoItems // Set to errNoItems to signal really nothing found, gets reset when name is matched.
-
- for _, svc := range serviceList {
- if !(match(r.namespace, svc.Namespace) && match(r.service, svc.Name)) {
- continue
- }
-
- // If namespace has a wildcard, filter results against Corefile namespace list.
- // (Namespaces without a wildcard were filtered before the call to this function.)
- if wildcard(r.namespace) && !k.namespaceExposed(svc.Namespace) {
- continue
- }
-
- // Endpoint query or headless service
- if svc.Spec.ClusterIP == api.ClusterIPNone || r.endpoint != "" {
- endpointsList := k.APIConn.EndpointsList()
- for _, ep := range endpointsList.Items {
- if ep.ObjectMeta.Name != svc.Name || ep.ObjectMeta.Namespace != svc.Namespace {
- continue
- }
-
- for _, eps := range ep.Subsets {
- for _, addr := range eps.Addresses {
-
- // See comments in parse.go parseRequest about the endpoint handling.
-
- if r.endpoint != "" {
- if !match(r.endpoint, endpointHostname(addr)) {
- continue
- }
- }
-
- for _, p := range eps.Ports {
- if !(match(r.port, p.Name) && match(r.protocol, string(p.Protocol))) {
- continue
- }
- s := msg.Service{Host: addr.IP, Port: int(p.Port), TTL: k.ttl}
- s.Key = strings.Join([]string{zonePath, Svc, svc.Namespace, svc.Name, endpointHostname(addr)}, "/")
-
- err = nil
-
- services = append(services, s)
- }
- }
- }
- }
- continue
- }
-
- // External service
- if svc.Spec.ExternalName != "" {
- s := msg.Service{Key: strings.Join([]string{zonePath, Svc, svc.Namespace, svc.Name}, "/"), Host: svc.Spec.ExternalName, TTL: k.ttl}
- if t, _ := s.HostType(); t == dns.TypeCNAME {
- s.Key = strings.Join([]string{zonePath, Svc, svc.Namespace, svc.Name}, "/")
- services = append(services, s)
-
- err = nil
-
- continue
- }
- }
-
- // ClusterIP service
- for _, p := range svc.Spec.Ports {
- if !(match(r.port, p.Name) && match(r.protocol, string(p.Protocol))) {
- continue
- }
-
- err = nil
-
- s := msg.Service{Host: svc.Spec.ClusterIP, Port: int(p.Port), TTL: k.ttl}
- s.Key = strings.Join([]string{zonePath, Svc, svc.Namespace, svc.Name}, "/")
-
- services = append(services, s)
- }
- }
- return services, err
-}
-
-// match checks if a and b are equal taking wildcards into account.
-func match(a, b string) bool {
- if wildcard(a) {
- return true
- }
- if wildcard(b) {
- return true
- }
- return strings.EqualFold(a, b)
-}
-
-// wildcard checks whether s contains a wildcard value defined as "*" or "any".
-func wildcard(s string) bool {
- return s == "*" || s == "any"
-}
-
-// namespaceExposed returns true when the namespace is exposed.
-func (k *Kubernetes) namespaceExposed(namespace string) bool {
- _, ok := k.Namespaces[namespace]
- if len(k.Namespaces) > 0 && !ok {
- return false
- }
- return true
-}
-
-const (
- // Svc is the DNS schema for kubernetes services
- Svc = "svc"
- // Pod is the DNS schema for kubernetes pods
- Pod = "pod"
- // defaultTTL to apply to all answers.
- defaultTTL = 5
-)
diff --git a/middleware/kubernetes/kubernetes_apex_test.go b/middleware/kubernetes/kubernetes_apex_test.go
deleted file mode 100644
index 9a87a790a..000000000
--- a/middleware/kubernetes/kubernetes_apex_test.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package kubernetes
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-var kubeApexCases = [](test.Case){
- {
- Qname: "cluster.local.", Qtype: dns.TypeSOA,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.SOA("cluster.local. 303 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 60"),
- },
- },
- {
- Qname: "cluster.local.", Qtype: dns.TypeHINFO,
- Rcode: dns.RcodeSuccess,
- Ns: []dns.RR{
- test.SOA("cluster.local. 303 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 60"),
- },
- },
- {
- Qname: "cluster.local.", Qtype: dns.TypeNS,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.NS("cluster.local. 303 IN NS ns.dns.cluster.local."),
- },
- Extra: []dns.RR{
- test.A("ns.dns.cluster.local. 303 IN A 127.0.0.1"),
- },
- },
-}
-
-func TestServeDNSApex(t *testing.T) {
-
- k := New([]string{"cluster.local."})
- k.APIConn = &APIConnServeTest{}
- k.Next = test.NextHandler(dns.RcodeSuccess, nil)
- ctx := context.TODO()
-
- for i, tc := range kubeApexCases {
- r := tc.Msg()
-
- w := dnsrecorder.New(&test.ResponseWriter{})
-
- _, err := k.ServeDNS(ctx, w, r)
- if err != tc.Error {
- t.Errorf("Test %d, expected no error, got %v\n", i, err)
- return
- }
- if tc.Error != nil {
- continue
- }
-
- resp := w.Msg
- if resp == nil {
- t.Fatalf("Test %d, got nil message and no error ford", i)
- }
-
- test.SortAndCheck(t, resp, tc)
- }
-}
diff --git a/middleware/kubernetes/kubernetes_test.go b/middleware/kubernetes/kubernetes_test.go
deleted file mode 100644
index 573a517b1..000000000
--- a/middleware/kubernetes/kubernetes_test.go
+++ /dev/null
@@ -1,242 +0,0 @@
-package kubernetes
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "k8s.io/client-go/1.5/pkg/api"
-)
-
-func TestWildcard(t *testing.T) {
- var tests = []struct {
- s string
- expected bool
- }{
- {"mynamespace", false},
- {"*", true},
- {"any", true},
- {"my*space", false},
- {"*space", false},
- {"myname*", false},
- }
-
- for _, te := range tests {
- got := wildcard(te.s)
- if got != te.expected {
- t.Errorf("Expected Wildcard result '%v' for example '%v', got '%v'.", te.expected, te.s, got)
- }
- }
-}
-
-func TestEndpointHostname(t *testing.T) {
- var tests = []struct {
- ip string
- hostname string
- expected string
- }{
- {"10.11.12.13", "", "10-11-12-13"},
- {"10.11.12.13", "epname", "epname"},
- }
- for _, test := range tests {
- result := endpointHostname(api.EndpointAddress{IP: test.ip, Hostname: test.hostname})
- if result != test.expected {
- t.Errorf("Expected endpoint name for (ip:%v hostname:%v) to be '%v', but got '%v'", test.ip, test.hostname, test.expected, result)
- }
- }
-}
-
-type APIConnServiceTest struct{}
-
-func (APIConnServiceTest) Run() { return }
-func (APIConnServiceTest) Stop() error { return nil }
-func (APIConnServiceTest) PodIndex(string) []interface{} { return nil }
-
-func (APIConnServiceTest) ServiceList() []*api.Service {
- svcs := []*api.Service{
- {
- ObjectMeta: api.ObjectMeta{
- Name: "svc1",
- Namespace: "testns",
- },
- Spec: api.ServiceSpec{
- ClusterIP: "10.0.0.1",
- Ports: []api.ServicePort{{
- Name: "http",
- Protocol: "tcp",
- Port: 80,
- }},
- },
- },
- {
- ObjectMeta: api.ObjectMeta{
- Name: "hdls1",
- Namespace: "testns",
- },
- Spec: api.ServiceSpec{
- ClusterIP: api.ClusterIPNone,
- },
- },
- {
- ObjectMeta: api.ObjectMeta{
- Name: "external",
- Namespace: "testns",
- },
- Spec: api.ServiceSpec{
- ExternalName: "coredns.io",
- Ports: []api.ServicePort{{
- Name: "http",
- Protocol: "tcp",
- Port: 80,
- }},
- },
- },
- }
- return svcs
-}
-
-func (APIConnServiceTest) EndpointsList() api.EndpointsList {
- n := "test.node.foo.bar"
-
- return api.EndpointsList{
- Items: []api.Endpoints{
- {
- Subsets: []api.EndpointSubset{
- {
- Addresses: []api.EndpointAddress{
- {
- IP: "172.0.0.1",
- Hostname: "ep1a",
- },
- },
- Ports: []api.EndpointPort{
- {
- Port: 80,
- Protocol: "tcp",
- Name: "http",
- },
- },
- },
- },
- ObjectMeta: api.ObjectMeta{
- Name: "svc1",
- Namespace: "testns",
- },
- },
- {
- Subsets: []api.EndpointSubset{
- {
- Addresses: []api.EndpointAddress{
- {
- IP: "172.0.0.2",
- },
- },
- Ports: []api.EndpointPort{
- {
- Port: 80,
- Protocol: "tcp",
- Name: "http",
- },
- },
- },
- },
- ObjectMeta: api.ObjectMeta{
- Name: "hdls1",
- Namespace: "testns",
- },
- },
- {
- Subsets: []api.EndpointSubset{
- {
- Addresses: []api.EndpointAddress{
- {
- IP: "172.0.0.3",
- },
- },
- Ports: []api.EndpointPort{
- {
- Port: 80,
- Protocol: "tcp",
- Name: "http",
- },
- },
- },
- },
- ObjectMeta: api.ObjectMeta{
- Name: "hdls1",
- Namespace: "testns",
- },
- },
- {
- Subsets: []api.EndpointSubset{
- {
- Addresses: []api.EndpointAddress{
- {
- IP: "10.9.8.7",
- NodeName: &n,
- },
- },
- },
- },
- },
- },
- }
-}
-
-func (APIConnServiceTest) GetNodeByName(name string) (api.Node, error) {
- return api.Node{
- ObjectMeta: api.ObjectMeta{
- Name: "test.node.foo.bar",
- },
- }, nil
-}
-
-func TestServices(t *testing.T) {
-
- k := New([]string{"interwebs.test."})
- k.APIConn = &APIConnServiceTest{}
-
- type svcAns struct {
- host string
- key string
- }
- type svcTest struct {
- qname string
- qtype uint16
- answer svcAns
- }
- tests := []svcTest{
- // Cluster IP Services
- {qname: "svc1.testns.svc.interwebs.test.", qtype: dns.TypeA, answer: svcAns{host: "10.0.0.1", key: "/coredns/test/interwebs/svc/testns/svc1"}},
- {qname: "_http._tcp.svc1.testns.svc.interwebs.test.", qtype: dns.TypeSRV, answer: svcAns{host: "10.0.0.1", key: "/coredns/test/interwebs/svc/testns/svc1"}},
- {qname: "ep1a.svc1.testns.svc.interwebs.test.", qtype: dns.TypeA, answer: svcAns{host: "172.0.0.1", key: "/coredns/test/interwebs/svc/testns/svc1/ep1a"}},
-
- // External Services
- {qname: "external.testns.svc.interwebs.test.", qtype: dns.TypeCNAME, answer: svcAns{host: "coredns.io", key: "/coredns/test/interwebs/svc/testns/external"}},
- }
-
- for i, test := range tests {
- state := request.Request{
- Req: &dns.Msg{Question: []dns.Question{{Name: test.qname, Qtype: test.qtype}}},
- Zone: "interwebs.test.", // must match from k.Zones[0]
- }
- svcs, e := k.Services(state, false, middleware.Options{})
- if e != nil {
- t.Errorf("Test %d: got error '%v'", i, e)
- continue
- }
- if len(svcs) != 1 {
- t.Errorf("Test %d, expected expected 1 answer, got %v", i, len(svcs))
- continue
- }
-
- if test.answer.host != svcs[0].Host {
- t.Errorf("Test %d, expected host '%v', got '%v'", i, test.answer.host, svcs[0].Host)
- }
- if test.answer.key != svcs[0].Key {
- t.Errorf("Test %d, expected key '%v', got '%v'", i, test.answer.key, svcs[0].Key)
- }
- }
-}
diff --git a/middleware/kubernetes/local.go b/middleware/kubernetes/local.go
deleted file mode 100644
index e5b7f1e0f..000000000
--- a/middleware/kubernetes/local.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package kubernetes
-
-import "net"
-
-func localPodIP() net.IP {
- addrs, err := net.InterfaceAddrs()
- if err != nil {
- return nil
- }
-
- for _, addr := range addrs {
- ip, _, _ := net.ParseCIDR(addr.String())
- ip = ip.To4()
- if ip == nil || ip.IsLoopback() {
- continue
- }
- return ip
- }
- return nil
-}
-
-func (k *Kubernetes) localNodeName() string {
- localIP := k.interfaceAddrsFunc()
- if localIP == nil {
- return ""
- }
-
- // Find endpoint matching localIP
- endpointsList := k.APIConn.EndpointsList()
- for _, ep := range endpointsList.Items {
- for _, eps := range ep.Subsets {
- for _, addr := range eps.Addresses {
- if localIP.Equal(net.ParseIP(addr.IP)) {
- return *addr.NodeName
- }
- }
- }
- }
- return ""
-}
diff --git a/middleware/kubernetes/ns.go b/middleware/kubernetes/ns.go
deleted file mode 100644
index 4cacc382f..000000000
--- a/middleware/kubernetes/ns.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package kubernetes
-
-import (
- "net"
- "strings"
-
- "github.com/miekg/dns"
- "k8s.io/client-go/1.5/pkg/api"
-)
-
-func isDefaultNS(name, zone string) bool {
- return strings.Index(name, defaultNSName) == 0 && strings.Index(name, zone) == len(defaultNSName)
-}
-
-func (k *Kubernetes) nsAddr() *dns.A {
- var (
- svcName string
- svcNamespace string
- )
-
- rr := new(dns.A)
- localIP := k.interfaceAddrsFunc()
- endpointsList := k.APIConn.EndpointsList()
-
- rr.A = localIP
-
-FindEndpoint:
- for _, ep := range endpointsList.Items {
- for _, eps := range ep.Subsets {
- for _, addr := range eps.Addresses {
- if localIP.Equal(net.ParseIP(addr.IP)) {
- svcNamespace = ep.ObjectMeta.Namespace
- svcName = ep.ObjectMeta.Name
- break FindEndpoint
- }
- }
- }
- }
-
- if len(svcName) == 0 {
- rr.Hdr.Name = defaultNSName
- rr.A = localIP
- return rr
- }
- // Find service to get ClusterIP
- serviceList := k.APIConn.ServiceList()
-
-FindService:
- for _, svc := range serviceList {
- if svcName == svc.Name && svcNamespace == svc.Namespace {
- if svc.Spec.ClusterIP == api.ClusterIPNone {
- rr.A = localIP
- } else {
- rr.A = net.ParseIP(svc.Spec.ClusterIP)
- }
- break FindService
- }
- }
-
- rr.Hdr.Name = strings.Join([]string{svcName, svcNamespace, "svc."}, ".")
-
- return rr
-}
-
-const defaultNSName = "ns.dns."
diff --git a/middleware/kubernetes/ns_test.go b/middleware/kubernetes/ns_test.go
deleted file mode 100644
index 8e9e80c71..000000000
--- a/middleware/kubernetes/ns_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package kubernetes
-
-import (
- "testing"
-
- "k8s.io/client-go/1.5/pkg/api"
-)
-
-type APIConnTest struct{}
-
-func (APIConnTest) Run() { return }
-func (APIConnTest) Stop() error { return nil }
-func (APIConnTest) PodIndex(string) []interface{} { return nil }
-
-func (APIConnTest) ServiceList() []*api.Service {
- svc := api.Service{
- ObjectMeta: api.ObjectMeta{
- Name: "dns-service",
- Namespace: "kube-system",
- },
- Spec: api.ServiceSpec{
- ClusterIP: "10.0.0.111",
- },
- }
-
- return []*api.Service{&svc}
-
-}
-
-func (APIConnTest) EndpointsList() api.EndpointsList {
- return api.EndpointsList{
- Items: []api.Endpoints{
- {
- Subsets: []api.EndpointSubset{
- {
- Addresses: []api.EndpointAddress{
- {
- IP: "127.0.0.1",
- },
- },
- },
- },
- ObjectMeta: api.ObjectMeta{
- Name: "dns-service",
- Namespace: "kube-system",
- },
- },
- },
- }
-}
-
-func (APIConnTest) GetNodeByName(name string) (api.Node, error) { return api.Node{}, nil }
-
-func TestNsAddr(t *testing.T) {
-
- k := New([]string{"inter.webs.test."})
- k.APIConn = &APIConnTest{}
-
- cdr := k.nsAddr()
- expected := "10.0.0.111"
-
- if cdr.A.String() != expected {
- t.Errorf("Expected A to be %q, got %q", expected, cdr.A.String())
- }
- expected = "dns-service.kube-system.svc."
- if cdr.Hdr.Name != expected {
- t.Errorf("Expected Hdr.Name to be %q, got %q", expected, cdr.Hdr.Name)
- }
-}
diff --git a/middleware/kubernetes/parse.go b/middleware/kubernetes/parse.go
deleted file mode 100644
index bf8adfec3..000000000
--- a/middleware/kubernetes/parse.go
+++ /dev/null
@@ -1,112 +0,0 @@
-package kubernetes
-
-import (
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-type recordRequest struct {
- // The named port from the kubernetes DNS spec, this is the service part (think _https) from a well formed
- // SRV record.
- port string
- // The protocol is usually _udp or _tcp (if set), and comes from the protocol part of a well formed
- // SRV record.
- protocol string
- endpoint string
- // The servicename used in Kubernetes.
- service string
- // The namespace used in Kubernetes.
- namespace string
- // A each name can be for a pod or a service, here we track what we've seen, either "pod" or "service".
- podOrSvc string
-}
-
-// parseRequest parses the qname to find all the elements we need for querying k8s. Anything
-// that is not parsed will have the wildcard "*" value (except r.endpoint).
-// Potential underscores are stripped from _port and _protocol.
-func parseRequest(state request.Request) (r recordRequest, err error) {
- // 3 Possible cases:
- // 1. _port._protocol.service.namespace.pod|svc.zone
- // 2. (endpoint): endpoint.service.namespace.pod|svc.zone
- // 3. (service): service.namespace.pod|svc.zone
- //
- // Federations are handled in the federation middleware. And aren't parsed here.
-
- base, _ := dnsutil.TrimZone(state.Name(), state.Zone)
- segs := dns.SplitDomainName(base)
-
- r.port = "*"
- r.protocol = "*"
- r.service = "*"
- r.namespace = "*"
- // r.endpoint is the odd one out, we need to know if it has been set or not. If it is
- // empty we should skip the endpoint check in k.get(). Hence we cannot set if to "*".
-
- // start at the right and fill out recordRequest with the bits we find, so we look for
- // pod|svc.namespace.service and then either
- // * endpoint
- // *_protocol._port
-
- last := len(segs) - 1
- if last < 0 {
- return r, nil
- }
- r.podOrSvc = segs[last]
- if r.podOrSvc != Pod && r.podOrSvc != Svc {
- return r, errInvalidRequest
- }
- last--
- if last < 0 {
- return r, nil
- }
-
- r.namespace = segs[last]
- last--
- if last < 0 {
- return r, nil
- }
-
- r.service = segs[last]
- last--
- if last < 0 {
- return r, nil
- }
-
- // Because of ambiquity we check the labels left: 1: an endpoint. 2: port and protocol.
- // Anything else is a query that is too long to answer and can safely be delegated to return an nxdomain.
- switch last {
-
- case 0: // endpoint only
- r.endpoint = segs[last]
- case 1: // service and port
- r.protocol = stripUnderscore(segs[last])
- r.port = stripUnderscore(segs[last-1])
-
- default: // too long
- return r, errInvalidRequest
- }
-
- return r, nil
-}
-
-// stripUnderscore removes a prefixed underscore from s.
-func stripUnderscore(s string) string {
- if s[0] != '_' {
- return s
- }
- return s[1:]
-}
-
-// String return a string representation of r, it just returns all fields concatenated with dots.
-// This is mostly used in tests.
-func (r recordRequest) String() string {
- s := r.port
- s += "." + r.protocol
- s += "." + r.endpoint
- s += "." + r.service
- s += "." + r.namespace
- s += "." + r.podOrSvc
- return s
-}
diff --git a/middleware/kubernetes/parse_test.go b/middleware/kubernetes/parse_test.go
deleted file mode 100644
index 06d5a2aaa..000000000
--- a/middleware/kubernetes/parse_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package kubernetes
-
-import (
- "testing"
-
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-func TestParseRequest(t *testing.T) {
- tests := []struct {
- query string
- expected string // output from r.String()
- }{
- // valid SRV request
- {"_http._tcp.webs.mynamespace.svc.inter.webs.test.", "http.tcp..webs.mynamespace.svc"},
- // wildcard acceptance
- {"*.any.*.any.svc.inter.webs.test.", "*.any..*.any.svc"},
- // A request of endpoint
- {"1-2-3-4.webs.mynamespace.svc.inter.webs.test.", "*.*.1-2-3-4.webs.mynamespace.svc"},
- }
- for i, tc := range tests {
- m := new(dns.Msg)
- m.SetQuestion(tc.query, dns.TypeA)
- state := request.Request{Zone: zone, Req: m}
-
- r, e := parseRequest(state)
- if e != nil {
- t.Errorf("Test %d, expected no error, got '%v'.", i, e)
- }
- rs := r.String()
- if rs != tc.expected {
- t.Errorf("Test %d, expected (stringyfied) recordRequest: %s, got %s", i, tc.expected, rs)
- }
- }
-}
-
-func TestParseInvalidRequest(t *testing.T) {
- invalid := []string{
- "webs.mynamespace.pood.inter.webs.test.", // Request must be for pod or svc subdomain.
- "too.long.for.what.I.am.trying.to.pod.inter.webs.tests.", // Too long.
- }
-
- for i, query := range invalid {
- m := new(dns.Msg)
- m.SetQuestion(query, dns.TypeA)
- state := request.Request{Zone: zone, Req: m}
-
- if _, e := parseRequest(state); e == nil {
- t.Errorf("Test %d: expected error from %s, got none", i, query)
- }
- }
-}
-
-const zone = "intern.webs.tests."
diff --git a/middleware/kubernetes/reverse.go b/middleware/kubernetes/reverse.go
deleted file mode 100644
index 25ece8035..000000000
--- a/middleware/kubernetes/reverse.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package kubernetes
-
-import (
- "strings"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/etcd/msg"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/request"
-)
-
-// Reverse implements the ServiceBackend interface.
-func (k *Kubernetes) Reverse(state request.Request, exact bool, opt middleware.Options) ([]msg.Service, error) {
-
- ip := dnsutil.ExtractAddressFromReverse(state.Name())
- if ip == "" {
- return nil, nil
- }
-
- records := k.serviceRecordForIP(ip, state.Name())
- return records, nil
-}
-
-// serviceRecordForIP gets a service record with a cluster ip matching the ip argument
-// If a service cluster ip does not match, it checks all endpoints
-func (k *Kubernetes) serviceRecordForIP(ip, name string) []msg.Service {
- // First check services with cluster ips
- svcList := k.APIConn.ServiceList()
-
- for _, service := range svcList {
- if (len(k.Namespaces) > 0) && !k.namespaceExposed(service.Namespace) {
- continue
- }
- if service.Spec.ClusterIP == ip {
- domain := strings.Join([]string{service.Name, service.Namespace, Svc, k.primaryZone()}, ".")
- return []msg.Service{{Host: domain}}
- }
- }
- // If no cluster ips match, search endpoints
- epList := k.APIConn.EndpointsList()
- for _, ep := range epList.Items {
- if (len(k.Namespaces) > 0) && !k.namespaceExposed(ep.ObjectMeta.Namespace) {
- continue
- }
- for _, eps := range ep.Subsets {
- for _, addr := range eps.Addresses {
- if addr.IP == ip {
- domain := strings.Join([]string{endpointHostname(addr), ep.ObjectMeta.Name, ep.ObjectMeta.Namespace, Svc, k.primaryZone()}, ".")
- return []msg.Service{{Host: domain}}
- }
- }
- }
- }
- return nil
-}
diff --git a/middleware/kubernetes/reverse_test.go b/middleware/kubernetes/reverse_test.go
deleted file mode 100644
index aaf0907e8..000000000
--- a/middleware/kubernetes/reverse_test.go
+++ /dev/null
@@ -1,125 +0,0 @@
-package kubernetes
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
- "k8s.io/client-go/1.5/pkg/api"
-)
-
-type APIConnReverseTest struct{}
-
-func (APIConnReverseTest) Run() { return }
-func (APIConnReverseTest) Stop() error { return nil }
-func (APIConnReverseTest) PodIndex(string) []interface{} { return nil }
-
-func (APIConnReverseTest) ServiceList() []*api.Service {
- svcs := []*api.Service{
- {
- ObjectMeta: api.ObjectMeta{
- Name: "svc1",
- Namespace: "testns",
- },
- Spec: api.ServiceSpec{
- ClusterIP: "192.168.1.100",
- Ports: []api.ServicePort{{
- Name: "http",
- Protocol: "tcp",
- Port: 80,
- }},
- },
- },
- }
- return svcs
-}
-
-func (APIConnReverseTest) EndpointsList() api.EndpointsList {
- return api.EndpointsList{
- Items: []api.Endpoints{
- {
- Subsets: []api.EndpointSubset{
- {
- Addresses: []api.EndpointAddress{
- {
- IP: "10.0.0.100",
- Hostname: "ep1a",
- },
- },
- Ports: []api.EndpointPort{
- {
- Port: 80,
- Protocol: "tcp",
- Name: "http",
- },
- },
- },
- },
- ObjectMeta: api.ObjectMeta{
- Name: "svc1",
- Namespace: "testns",
- },
- },
- },
- }
-}
-
-func (APIConnReverseTest) GetNodeByName(name string) (api.Node, error) {
- return api.Node{
- ObjectMeta: api.ObjectMeta{
- Name: "test.node.foo.bar",
- },
- }, nil
-}
-
-func TestReverse(t *testing.T) {
-
- k := New([]string{"cluster.local.", "0.10.in-addr.arpa."})
- k.APIConn = &APIConnReverseTest{}
-
- tests := []test.Case{
- {
- Qname: "100.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
- Rcode: dns.RcodeSuccess,
- Answer: []dns.RR{
- test.PTR("100.0.0.10.in-addr.arpa. 303 IN PTR ep1a.svc1.testns.svc.cluster.local."),
- },
- },
- {
- Qname: "101.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
- Rcode: dns.RcodeSuccess,
- Ns: []dns.RR{
- test.SOA("0.10.in-addr.arpa. 300 IN SOA ns.dns.0.10.in-addr.arpa. hostmaster.0.10.in-addr.arpa. 1502782828 7200 1800 86400 60"),
- },
- },
- {
- Qname: "example.org.cluster.local.", Qtype: dns.TypePTR,
- Rcode: dns.RcodeSuccess,
- Ns: []dns.RR{
- test.SOA("cluster.local. 300 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1502989566 7200 1800 86400 60"),
- },
- },
- }
-
- ctx := context.TODO()
- for i, tc := range tests {
- r := tc.Msg()
-
- w := dnsrecorder.New(&test.ResponseWriter{})
-
- _, err := k.ServeDNS(ctx, w, r)
- if err != tc.Error {
- t.Errorf("Test %d: expected no error, got %v", i, err)
- return
- }
-
- resp := w.Msg
- if resp == nil {
- t.Fatalf("Test %d: got nil message and no error for: %s %d", i, r.Question[0].Name, r.Question[0].Qtype)
- }
- test.SortAndCheck(t, resp, tc)
- }
-}
diff --git a/middleware/kubernetes/setup.go b/middleware/kubernetes/setup.go
deleted file mode 100644
index 15b87c2cd..000000000
--- a/middleware/kubernetes/setup.go
+++ /dev/null
@@ -1,208 +0,0 @@
-package kubernetes
-
-import (
- "errors"
- "fmt"
- "strconv"
- "strings"
- "time"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/middleware/proxy"
- "github.com/miekg/dns"
-
- "github.com/mholt/caddy"
- unversionedapi "k8s.io/client-go/1.5/pkg/api/unversioned"
-)
-
-func init() {
- caddy.RegisterPlugin("kubernetes", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- kubernetes, initOpts, err := kubernetesParse(c)
- if err != nil {
- return middleware.Error("kubernetes", err)
- }
-
- err = kubernetes.initKubeCache(initOpts)
- if err != nil {
- return middleware.Error("kubernetes", err)
- }
-
- // Register KubeCache start and stop functions with Caddy
- c.OnStartup(func() error {
- go kubernetes.APIConn.Run()
- if kubernetes.APIProxy != nil {
- go kubernetes.APIProxy.Run()
- }
- return nil
- })
-
- c.OnShutdown(func() error {
- if kubernetes.APIProxy != nil {
- kubernetes.APIProxy.Stop()
- }
- return kubernetes.APIConn.Stop()
- })
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- kubernetes.Next = next
- return kubernetes
- })
-
- return nil
-}
-
-func kubernetesParse(c *caddy.Controller) (*Kubernetes, dnsControlOpts, error) {
- k8s := New([]string{""})
- k8s.interfaceAddrsFunc = localPodIP
- k8s.autoPathSearch = searchFromResolvConf()
-
- opts := dnsControlOpts{
- resyncPeriod: defaultResyncPeriod,
- }
-
- for c.Next() {
- zones := c.RemainingArgs()
-
- if len(zones) != 0 {
- k8s.Zones = zones
- for i := 0; i < len(k8s.Zones); i++ {
- k8s.Zones[i] = middleware.Host(k8s.Zones[i]).Normalize()
- }
- } else {
- k8s.Zones = make([]string, len(c.ServerBlockKeys))
- for i := 0; i < len(c.ServerBlockKeys); i++ {
- k8s.Zones[i] = middleware.Host(c.ServerBlockKeys[i]).Normalize()
- }
- }
-
- k8s.primaryZoneIndex = -1
- for i, z := range k8s.Zones {
- if strings.HasSuffix(z, "in-addr.arpa.") || strings.HasSuffix(z, "ip6.arpa.") {
- continue
- }
- k8s.primaryZoneIndex = i
- break
- }
-
- if k8s.primaryZoneIndex == -1 {
- return nil, opts, errors.New("non-reverse zone name must be used")
- }
-
- for c.NextBlock() {
- switch c.Val() {
- case "pods":
- args := c.RemainingArgs()
- if len(args) == 1 {
- switch args[0] {
- case podModeDisabled, podModeInsecure, podModeVerified:
- k8s.podMode = args[0]
- default:
- return nil, opts, fmt.Errorf("wrong value for pods: %s, must be one of: disabled, verified, insecure", args[0])
- }
- continue
- }
- return nil, opts, c.ArgErr()
- case "namespaces":
- args := c.RemainingArgs()
- if len(args) > 0 {
- for _, a := range args {
- k8s.Namespaces[a] = true
- }
- continue
- }
- return nil, opts, c.ArgErr()
- case "endpoint":
- args := c.RemainingArgs()
- if len(args) > 0 {
- for _, endpoint := range strings.Split(args[0], ",") {
- k8s.APIServerList = append(k8s.APIServerList, strings.TrimSpace(endpoint))
- }
- continue
- }
- return nil, opts, c.ArgErr()
- case "tls": // cert key cacertfile
- args := c.RemainingArgs()
- if len(args) == 3 {
- k8s.APIClientCert, k8s.APIClientKey, k8s.APICertAuth = args[0], args[1], args[2]
- continue
- }
- return nil, opts, c.ArgErr()
- case "resyncperiod":
- args := c.RemainingArgs()
- if len(args) > 0 {
- rp, err := time.ParseDuration(args[0])
- if err != nil {
- return nil, opts, fmt.Errorf("unable to parse resync duration value: '%v': %v", args[0], err)
- }
- opts.resyncPeriod = rp
- continue
- }
- return nil, opts, c.ArgErr()
- case "labels":
- args := c.RemainingArgs()
- if len(args) > 0 {
- labelSelectorString := strings.Join(args, " ")
- ls, err := unversionedapi.ParseToLabelSelector(labelSelectorString)
- if err != nil {
- return nil, opts, fmt.Errorf("unable to parse label selector value: '%v': %v", labelSelectorString, err)
- }
- opts.labelSelector = ls
- continue
- }
- return nil, opts, c.ArgErr()
- case "fallthrough":
- args := c.RemainingArgs()
- if len(args) == 0 {
- k8s.Fallthrough = true
- continue
- }
- return nil, opts, c.ArgErr()
- case "upstream":
- args := c.RemainingArgs()
- if len(args) == 0 {
- return nil, opts, c.ArgErr()
- }
- ups, err := dnsutil.ParseHostPortOrFile(args...)
- if err != nil {
- return nil, opts, err
- }
- k8s.Proxy = proxy.NewLookup(ups)
- case "ttl":
- args := c.RemainingArgs()
- if len(args) == 0 {
- return nil, opts, c.ArgErr()
- }
- t, err := strconv.Atoi(args[0])
- if err != nil {
- return nil, opts, err
- }
- if t < 5 || t > 3600 {
- return nil, opts, c.Errf("ttl must be in range [5, 3600]: %d", t)
- }
- k8s.ttl = uint32(t)
- default:
- return nil, opts, c.Errf("unknown property '%s'", c.Val())
- }
- }
- }
- return k8s, opts, nil
-}
-
-func searchFromResolvConf() []string {
- rc, err := dns.ClientConfigFromFile("/etc/resolv.conf")
- if err != nil {
- return nil
- }
- middleware.Zones(rc.Search).Normalize()
- return rc.Search
-}
-
-const defaultResyncPeriod = 5 * time.Minute
diff --git a/middleware/kubernetes/setup_reverse_test.go b/middleware/kubernetes/setup_reverse_test.go
deleted file mode 100644
index ed51a7410..000000000
--- a/middleware/kubernetes/setup_reverse_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package kubernetes
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestKubernetesParseReverseZone(t *testing.T) {
- tests := []struct {
- input string // Corefile data as string
- expectedZones []string // expected count of defined zones.
- }{
- {`kubernetes coredns.local 10.0.0.0/16`, []string{"coredns.local.", "0.10.in-addr.arpa."}},
- {`kubernetes coredns.local 10.0.0.0/17`, []string{"coredns.local.", "10.0.0.0/17."}},
- }
-
- for i, tc := range tests {
- c := caddy.NewTestController("dns", tc.input)
- k, _, err := kubernetesParse(c)
- if err != nil {
- t.Fatalf("Test %d: Expected no error, got %q", i, err)
- }
-
- zl := len(k.Zones)
- if zl != len(tc.expectedZones) {
- t.Errorf("Test %d: Expected kubernetes to be initialized with %d zones, found %d zones", i, len(tc.expectedZones), zl)
- }
- for i, z := range tc.expectedZones {
- if k.Zones[i] != z {
- t.Errorf("Test %d: Expected zones to be %q, got %q", i, z, k.Zones[i])
- }
- }
- }
-}
diff --git a/middleware/kubernetes/setup_test.go b/middleware/kubernetes/setup_test.go
deleted file mode 100644
index 2fdc38a9c..000000000
--- a/middleware/kubernetes/setup_test.go
+++ /dev/null
@@ -1,473 +0,0 @@
-package kubernetes
-
-import (
- "strings"
- "testing"
- "time"
-
- "github.com/mholt/caddy"
- "k8s.io/client-go/1.5/pkg/api/unversioned"
-)
-
-func TestKubernetesParse(t *testing.T) {
- tests := []struct {
- input string // Corefile data as string
- shouldErr bool // true if test case is exected to produce an error.
- expectedErrContent string // substring from the expected error. Empty for positive cases.
- expectedZoneCount int // expected count of defined zones.
- expectedNSCount int // expected count of namespaces.
- expectedResyncPeriod time.Duration // expected resync period value
- expectedLabelSelector string // expected label selector value
- expectedPodMode string
- expectedFallthrough bool
- expectedUpstreams []string
- }{
- // positive
- {
- `kubernetes coredns.local`,
- false,
- "",
- 1,
- 0,
- defaultResyncPeriod,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local test.local`,
- false,
- "",
- 2,
- 0,
- defaultResyncPeriod,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
-}`,
- false,
- "",
- 1,
- 0,
- defaultResyncPeriod,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
- endpoint http://localhost:9090
-}`,
- false,
- "",
- 1,
- 0,
- defaultResyncPeriod,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
- namespaces demo
-}`,
- false,
- "",
- 1,
- 1,
- defaultResyncPeriod,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
- namespaces demo test
-}`,
- false,
- "",
- 1,
- 2,
- defaultResyncPeriod,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
- resyncperiod 30s
-}`,
- false,
- "",
- 1,
- 0,
- 30 * time.Second,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
- resyncperiod 15m
-}`,
- false,
- "",
- 1,
- 0,
- 15 * time.Minute,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
- labels environment=prod
-}`,
- false,
- "",
- 1,
- 0,
- defaultResyncPeriod,
- "environment=prod",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
- labels environment in (production, staging, qa),application=nginx
-}`,
- false,
- "",
- 1,
- 0,
- defaultResyncPeriod,
- "application=nginx,environment in (production,qa,staging)",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local test.local {
- resyncperiod 15m
- endpoint http://localhost:8080
- namespaces demo test
- labels environment in (production, staging, qa),application=nginx
- fallthrough
-}`,
- false,
- "",
- 2,
- 2,
- 15 * time.Minute,
- "application=nginx,environment in (production,qa,staging)",
- podModeDisabled,
- true,
- nil,
- },
- // negative
- {
- `kubernetes coredns.local {
- endpoint
-}`,
- true,
- "rong argument count or unexpected line ending",
- -1,
- -1,
- defaultResyncPeriod,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
- namespaces
-}`,
- true,
- "rong argument count or unexpected line ending",
- -1,
- -1,
- defaultResyncPeriod,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
- resyncperiod
-}`,
- true,
- "rong argument count or unexpected line ending",
- -1,
- 0,
- 0 * time.Minute,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
- resyncperiod 15
-}`,
- true,
- "unable to parse resync duration value",
- -1,
- 0,
- 0 * time.Second,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
- resyncperiod abc
-}`,
- true,
- "unable to parse resync duration value",
- -1,
- 0,
- 0 * time.Second,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
- labels
-}`,
- true,
- "rong argument count or unexpected line ending",
- -1,
- 0,
- 0 * time.Second,
- "",
- podModeDisabled,
- false,
- nil,
- },
- {
- `kubernetes coredns.local {
- labels environment in (production, qa
-}`,
- true,
- "unable to parse label selector",
- -1,
- 0,
- 0 * time.Second,
- "",
- podModeDisabled,
- false,
- nil,
- },
- // pods disabled
- {
- `kubernetes coredns.local {
- pods disabled
-}`,
- false,
- "",
- 1,
- 0,
- defaultResyncPeriod,
- "",
- podModeDisabled,
- false,
- nil,
- },
- // pods insecure
- {
- `kubernetes coredns.local {
- pods insecure
-}`,
- false,
- "",
- 1,
- 0,
- defaultResyncPeriod,
- "",
- podModeInsecure,
- false,
- nil,
- },
- // pods verified
- {
- `kubernetes coredns.local {
- pods verified
-}`,
- false,
- "",
- 1,
- 0,
- defaultResyncPeriod,
- "",
- podModeVerified,
- false,
- nil,
- },
- // pods invalid
- {
- `kubernetes coredns.local {
- pods giant_seed
-}`,
- true,
- "rong value for pods",
- -1,
- 0,
- defaultResyncPeriod,
- "",
- podModeVerified,
- false,
- nil,
- },
- // fallthrough invalid
- {
- `kubernetes coredns.local {
- fallthrough junk
-}`,
- true,
- "rong argument count",
- -1,
- 0,
- defaultResyncPeriod,
- "",
- podModeDisabled,
- false,
- nil,
- },
- // Valid upstream
- {
- `kubernetes coredns.local {
- upstream 13.14.15.16:53
-}`,
- false,
- "",
- 1,
- 0,
- defaultResyncPeriod,
- "",
- podModeDisabled,
- false,
- []string{"13.14.15.16:53"},
- },
- // Invalid upstream
- {
- `kubernetes coredns.local {
- upstream 13.14.15.16orange
-}`,
- true,
- "not an IP address or file: \"13.14.15.16orange\"",
- -1,
- 0,
- defaultResyncPeriod,
- "",
- podModeDisabled,
- false,
- nil,
- },
- }
-
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- k8sController, opts, err := kubernetesParse(c)
-
- if test.shouldErr && err == nil {
- t.Errorf("Test %d: Expected error, but did not find error for input '%s'. Error was: '%v'", i, test.input, err)
- }
-
- if err != nil {
- if !test.shouldErr {
- t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
- continue
- }
-
- if test.shouldErr && (len(test.expectedErrContent) < 1) {
- t.Fatalf("Test %d: Test marked as expecting an error, but no expectedErrContent provided for input '%s'. Error was: '%v'", i, test.input, err)
- }
-
- if test.shouldErr && (test.expectedZoneCount >= 0) {
- t.Errorf("Test %d: Test marked as expecting an error, but provides value for expectedZoneCount!=-1 for input '%s'. Error was: '%v'", i, test.input, err)
- }
-
- if !strings.Contains(err.Error(), test.expectedErrContent) {
- t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input)
- }
- continue
- }
-
- // No error was raised, so validate initialization of k8sController
- // Zones
- foundZoneCount := len(k8sController.Zones)
- if foundZoneCount != test.expectedZoneCount {
- t.Errorf("Test %d: Expected kubernetes controller to be initialized with %d zones, instead found %d zones: '%v' for input '%s'", i, test.expectedZoneCount, foundZoneCount, k8sController.Zones, test.input)
- }
-
- // Namespaces
- foundNSCount := len(k8sController.Namespaces)
- if foundNSCount != test.expectedNSCount {
- t.Errorf("Test %d: Expected kubernetes controller to be initialized with %d namespaces. Instead found %d namespaces: '%v' for input '%s'", i, test.expectedNSCount, foundNSCount, k8sController.Namespaces, test.input)
- }
-
- // ResyncPeriod
- foundResyncPeriod := opts.resyncPeriod
- if foundResyncPeriod != test.expectedResyncPeriod {
- t.Errorf("Test %d: Expected kubernetes controller to be initialized with resync period '%s'. Instead found period '%s' for input '%s'", i, test.expectedResyncPeriod, foundResyncPeriod, test.input)
- }
-
- // Labels
- if opts.labelSelector != nil {
- foundLabelSelectorString := unversioned.FormatLabelSelector(opts.labelSelector)
- if foundLabelSelectorString != test.expectedLabelSelector {
- t.Errorf("Test %d: Expected kubernetes controller to be initialized with label selector '%s'. Instead found selector '%s' for input '%s'", i, test.expectedLabelSelector, foundLabelSelectorString, test.input)
- }
- }
- // Pods
- foundPodMode := k8sController.podMode
- if foundPodMode != test.expectedPodMode {
- t.Errorf("Test %d: Expected kubernetes controller to be initialized with pod mode '%s'. Instead found pod mode '%s' for input '%s'", i, test.expectedPodMode, foundPodMode, test.input)
- }
-
- // fallthrough
- foundFallthrough := k8sController.Fallthrough
- if foundFallthrough != test.expectedFallthrough {
- t.Errorf("Test %d: Expected kubernetes controller to be initialized with fallthrough '%v'. Instead found fallthrough '%v' for input '%s'", i, test.expectedFallthrough, foundFallthrough, test.input)
- }
- // upstream
- foundUpstreams := k8sController.Proxy.Upstreams
- if test.expectedUpstreams == nil {
- if foundUpstreams != nil {
- t.Errorf("Test %d: Expected kubernetes controller to not be initialized with upstreams for input '%s'", i, test.input)
- }
- } else {
- if foundUpstreams == nil {
- t.Errorf("Test %d: Expected kubernetes controller to be initialized with upstreams for input '%s'", i, test.input)
- } else {
- if len(*foundUpstreams) != len(test.expectedUpstreams) {
- t.Errorf("Test %d: Expected kubernetes controller to be initialized with %d upstreams. Instead found %d upstreams for input '%s'", i, len(test.expectedUpstreams), len(*foundUpstreams), test.input)
- }
- for j, want := range test.expectedUpstreams {
- got := (*foundUpstreams)[j].Select().Name
- if got != want {
- t.Errorf("Test %d: Expected kubernetes controller to be initialized with upstream '%s'. Instead found upstream '%s' for input '%s'", i, want, got, test.input)
- }
- }
-
- }
- }
- }
-}
diff --git a/middleware/kubernetes/setup_ttl_test.go b/middleware/kubernetes/setup_ttl_test.go
deleted file mode 100644
index d58f91576..000000000
--- a/middleware/kubernetes/setup_ttl_test.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package kubernetes
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestKubernetesParseTTL(t *testing.T) {
- tests := []struct {
- input string // Corefile data as string
- expectedTTL uint32 // expected count of defined zones.
- shouldErr bool
- }{
- {`kubernetes cluster.local {
- ttl 56
- }`, 56, false},
- {`kubernetes cluster.local`, defaultTTL, false},
- {`kubernetes cluster.local {
- ttl -1
- }`, 0, true},
- {`kubernetes cluster.local {
- ttl 3601
- }`, 0, true},
- }
-
- for i, tc := range tests {
- c := caddy.NewTestController("dns", tc.input)
- k, _, err := kubernetesParse(c)
- if err != nil && !tc.shouldErr {
- t.Fatalf("Test %d: Expected no error, got %q", i, err)
- }
- if err == nil && tc.shouldErr {
- t.Fatalf("Test %d: Expected error, got none", i)
- }
- if err != nil && tc.shouldErr {
- // input should error
- continue
- }
-
- if k.ttl != tc.expectedTTL {
- t.Errorf("Test %d: Expected TTl to be %d, got %d", i, tc.expectedTTL, k.ttl)
- }
- }
-}
diff --git a/middleware/loadbalance/README.md b/middleware/loadbalance/README.md
deleted file mode 100644
index 1cce54ebf..000000000
--- a/middleware/loadbalance/README.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# loadbalance
-
-*loadbalance* acts as a round-robin DNS loadbalancer by randomizing the order of A and AAAA records
- in the answer.
-
- See [Wikipedia](https://en.wikipedia.org/wiki/Round-robin_DNS) about the pros and cons on this
- setup. It will take care to sort any CNAMEs before any address records, because some stub resolver
- implementations (like glibc) are particular about that.
-
-## Syntax
-
-~~~
-loadbalance [POLICY]
-~~~
-
-* **POLICY** is how to balance, the default is "round_robin"
-
-## Examples
-
-~~~
-loadbalance round_robin
-~~~
diff --git a/middleware/loadbalance/handler.go b/middleware/loadbalance/handler.go
deleted file mode 100644
index 3d3e4aa26..000000000
--- a/middleware/loadbalance/handler.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Package loadbalance is middleware for rewriting responses to do "load balancing"
-package loadbalance
-
-import (
- "github.com/coredns/coredns/middleware"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// RoundRobin is middleware to rewrite responses for "load balancing".
-type RoundRobin struct {
- Next middleware.Handler
-}
-
-// ServeDNS implements the middleware.Handler interface.
-func (rr RoundRobin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- wrr := &RoundRobinResponseWriter{w}
- return middleware.NextOrFailure(rr.Name(), rr.Next, ctx, wrr, r)
-}
-
-// Name implements the Handler interface.
-func (rr RoundRobin) Name() string { return "loadbalance" }
diff --git a/middleware/loadbalance/loadbalance.go b/middleware/loadbalance/loadbalance.go
deleted file mode 100644
index 7df0b31c6..000000000
--- a/middleware/loadbalance/loadbalance.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// Package loadbalance shuffles A and AAAA records.
-package loadbalance
-
-import (
- "log"
-
- "github.com/miekg/dns"
-)
-
-// RoundRobinResponseWriter is a response writer that shuffles A and AAAA records.
-type RoundRobinResponseWriter struct {
- dns.ResponseWriter
-}
-
-// WriteMsg implements the dns.ResponseWriter interface.
-func (r *RoundRobinResponseWriter) WriteMsg(res *dns.Msg) error {
- if res.Rcode != dns.RcodeSuccess {
- return r.ResponseWriter.WriteMsg(res)
- }
-
- res.Answer = roundRobin(res.Answer)
- res.Ns = roundRobin(res.Ns)
- res.Extra = roundRobin(res.Extra)
-
- return r.ResponseWriter.WriteMsg(res)
-}
-
-func roundRobin(in []dns.RR) []dns.RR {
- cname := []dns.RR{}
- address := []dns.RR{}
- mx := []dns.RR{}
- rest := []dns.RR{}
- for _, r := range in {
- switch r.Header().Rrtype {
- case dns.TypeCNAME:
- cname = append(cname, r)
- case dns.TypeA, dns.TypeAAAA:
- address = append(address, r)
- case dns.TypeMX:
- mx = append(mx, r)
- default:
- rest = append(rest, r)
- }
- }
-
- roundRobinShuffle(address)
- roundRobinShuffle(mx)
-
- out := append(cname, rest...)
- out = append(out, address...)
- out = append(out, mx...)
- return out
-}
-
-func roundRobinShuffle(records []dns.RR) {
- switch l := len(records); l {
- case 0, 1:
- break
- case 2:
- if dns.Id()%2 == 0 {
- records[0], records[1] = records[1], records[0]
- }
- default:
- for j := 0; j < l*(int(dns.Id())%4+1); j++ {
- q := int(dns.Id()) % l
- p := int(dns.Id()) % l
- if q == p {
- p = (p + 1) % l
- }
- records[q], records[p] = records[p], records[q]
- }
- }
-}
-
-// Write implements the dns.ResponseWriter interface.
-func (r *RoundRobinResponseWriter) Write(buf []byte) (int, error) {
- // Should we pack and unpack here to fiddle with the packet... Not likely.
- log.Printf("[WARNING] RoundRobin called with Write: no shuffling records")
- n, err := r.ResponseWriter.Write(buf)
- return n, err
-}
-
-// Hijack implements the dns.ResponseWriter interface.
-func (r *RoundRobinResponseWriter) Hijack() {
- r.ResponseWriter.Hijack()
- return
-}
diff --git a/middleware/loadbalance/loadbalance_test.go b/middleware/loadbalance/loadbalance_test.go
deleted file mode 100644
index 8c9205ca9..000000000
--- a/middleware/loadbalance/loadbalance_test.go
+++ /dev/null
@@ -1,168 +0,0 @@
-package loadbalance
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func TestLoadBalance(t *testing.T) {
- rm := RoundRobin{Next: handler()}
-
- // the first X records must be cnames after this test
- tests := []struct {
- answer []dns.RR
- extra []dns.RR
- cnameAnswer int
- cnameExtra int
- addressAnswer int
- addressExtra int
- mxAnswer int
- mxExtra int
- }{
- {
- answer: []dns.RR{
- test.CNAME("cname1.region2.skydns.test. 300 IN CNAME cname2.region2.skydns.test."),
- test.CNAME("cname2.region2.skydns.test. 300 IN CNAME cname3.region2.skydns.test."),
- test.CNAME("cname5.region2.skydns.test. 300 IN CNAME cname6.region2.skydns.test."),
- test.CNAME("cname6.region2.skydns.test. 300 IN CNAME endpoint.region2.skydns.test."),
- test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.1"),
- test.MX("mx.region2.skydns.test. 300 IN MX 1 mx1.region2.skydns.test."),
- test.MX("mx.region2.skydns.test. 300 IN MX 2 mx2.region2.skydns.test."),
- test.MX("mx.region2.skydns.test. 300 IN MX 3 mx3.region2.skydns.test."),
- },
- cnameAnswer: 4,
- addressAnswer: 1,
- mxAnswer: 3,
- },
- {
- answer: []dns.RR{
- test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.1"),
- test.MX("mx.region2.skydns.test. 300 IN MX 1 mx1.region2.skydns.test."),
- test.CNAME("cname.region2.skydns.test. 300 IN CNAME endpoint.region2.skydns.test."),
- },
- cnameAnswer: 1,
- addressAnswer: 1,
- mxAnswer: 1,
- },
- {
- answer: []dns.RR{
- test.MX("mx.region2.skydns.test. 300 IN MX 1 mx1.region2.skydns.test."),
- test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.1"),
- test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.2"),
- test.MX("mx.region2.skydns.test. 300 IN MX 1 mx2.region2.skydns.test."),
- test.CNAME("cname2.region2.skydns.test. 300 IN CNAME cname3.region2.skydns.test."),
- test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.3"),
- test.MX("mx.region2.skydns.test. 300 IN MX 1 mx3.region2.skydns.test."),
- },
- extra: []dns.RR{
- test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.1"),
- test.AAAA("endpoint.region2.skydns.test. 300 IN AAAA ::1"),
- test.MX("mx.region2.skydns.test. 300 IN MX 1 mx1.region2.skydns.test."),
- test.CNAME("cname2.region2.skydns.test. 300 IN CNAME cname3.region2.skydns.test."),
- test.MX("mx.region2.skydns.test. 300 IN MX 1 mx2.region2.skydns.test."),
- test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.3"),
- test.AAAA("endpoint.region2.skydns.test. 300 IN AAAA ::2"),
- test.MX("mx.region2.skydns.test. 300 IN MX 1 mx3.region2.skydns.test."),
- },
- cnameAnswer: 1,
- cnameExtra: 1,
- addressAnswer: 3,
- addressExtra: 4,
- mxAnswer: 3,
- mxExtra: 3,
- },
- }
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
-
- for i, test := range tests {
- req := new(dns.Msg)
- req.SetQuestion("region2.skydns.test.", dns.TypeSRV)
- req.Answer = test.answer
- req.Extra = test.extra
-
- _, err := rm.ServeDNS(context.TODO(), rec, req)
- if err != nil {
- t.Errorf("Test %d: Expected no error, but got %s", i, err)
- continue
-
- }
-
- cname, address, mx, sorted := countRecords(rec.Msg.Answer)
- if !sorted {
- t.Errorf("Test %d: Expected CNAMEs, then AAAAs, then MX in Answer, but got mixed", i)
- }
- if cname != test.cnameAnswer {
- t.Errorf("Test %d: Expected %d CNAMEs in Answer, but got %d", i, test.cnameAnswer, cname)
- }
- if address != test.addressAnswer {
- t.Errorf("Test %d: Expected %d A/AAAAs in Answer, but got %d", i, test.addressAnswer, address)
- }
- if mx != test.mxAnswer {
- t.Errorf("Test %d: Expected %d MXs in Answer, but got %d", i, test.mxAnswer, mx)
- }
-
- cname, address, mx, sorted = countRecords(rec.Msg.Extra)
- if !sorted {
- t.Errorf("Test %d: Expected CNAMEs, then AAAAs, then MX in Extra, but got mixed", i)
- }
- if cname != test.cnameExtra {
- t.Errorf("Test %d: Expected %d CNAMEs in Extra, but got %d", i, test.cnameAnswer, cname)
- }
- if address != test.addressExtra {
- t.Errorf("Test %d: Expected %d A/AAAAs in Extra, but got %d", i, test.addressAnswer, address)
- }
- if mx != test.mxExtra {
- t.Errorf("Test %d: Expected %d MXs in Extra, but got %d", i, test.mxAnswer, mx)
- }
- }
-}
-
-func countRecords(result []dns.RR) (cname int, address int, mx int, sorted bool) {
- const (
- Start = iota
- CNAMERecords
- ARecords
- MXRecords
- Any
- )
-
- // The order of the records is used to determine if the round-robin actually did anything.
- sorted = true
- cname = 0
- address = 0
- mx = 0
- state := Start
- for _, r := range result {
- switch r.Header().Rrtype {
- case dns.TypeCNAME:
- sorted = sorted && state <= CNAMERecords
- state = CNAMERecords
- cname++
- case dns.TypeA, dns.TypeAAAA:
- sorted = sorted && state <= ARecords
- state = ARecords
- address++
- case dns.TypeMX:
- sorted = sorted && state <= MXRecords
- state = MXRecords
- mx++
- default:
- state = Any
- }
- }
- return
-}
-
-func handler() middleware.Handler {
- return middleware.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- w.WriteMsg(r)
- return dns.RcodeSuccess, nil
- })
-}
diff --git a/middleware/loadbalance/setup.go b/middleware/loadbalance/setup.go
deleted file mode 100644
index 334d288a0..000000000
--- a/middleware/loadbalance/setup.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package loadbalance
-
-import (
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("loadbalance", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- for c.Next() {
- // TODO(miek): block and option parsing
- }
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- return RoundRobin{Next: next}
- })
-
- return nil
-}
diff --git a/middleware/log/README.md b/middleware/log/README.md
deleted file mode 100644
index 223888ccc..000000000
--- a/middleware/log/README.md
+++ /dev/null
@@ -1,102 +0,0 @@
-# log
-
-*log* enables query logging to standard output.
-
-## Syntax
-
-~~~ txt
-log
-~~~
-
-* With no arguments, a query log entry is written to *stdout* in the common log format for all requests
-
-~~~ txt
-log FILE
-~~~
-
-* **FILE** is the log file to create (or append to). The *only* valid name for **FILE** is *stdout*.
-
-~~~ txt
-log [NAME] FILE [FORMAT]
-~~~
-
-* `NAME` is the name to match in order to be logged
-* `FILE` is the log file (again only *stdout* is allowed here).
-* `FORMAT` is the log format to use (default is Common Log Format)
-
-You can further specify the class of responses that get logged:
-
-~~~ txt
-log [NAME] FILE [FORMAT] {
- class [success|denial|error|all]
-}
-~~~
-
-Here `success` `denial` and `error` denotes the class of responses that should be logged. The
-classes have the following meaning:
-
-* `success`: successful response
-* `denial`: either NXDOMAIN or NODATA (name exists, type does not)
-* `error`: SERVFAIL, NOTIMP, REFUSED, etc. Anything that indicates the remote server is not willing to
- resolve the request.
-* `all`: the default - nothing is specified.
-
-If no class is specified, it defaults to *all*.
-
-## Log File
-
-The "log file" can only be *stdout*. CoreDNS expects another service to pick up this output and deal
-with it, i.e. journald when using systemd or Docker's logging capabilities.
-
-## Log Format
-
-You can specify a custom log format with any placeholder values. Log supports both request and
-response placeholders.
-
-The following place holders are supported:
-
-* `{type}`: qtype of the request
-* `{name}`: qname of the request
-* `{class}`: qclass of the request
-* `{proto}`: protocol used (tcp or udp)
-* `{when}`: time of the query
-* `{remote}`: client's IP address
-* `{size}`: request size in bytes
-* `{port}`: client's port
-* `{duration}`: response duration
-* `{rcode}`: response RCODE
-* `{rsize}`: response size
-* `{>rflags}`: response flags, each set flag will be displayed, e.g. "aa, tc". This includes the qr
- bit as well.
-* `{>bufsize}`: the EDNS0 buffer size advertised in the query
-* `{>do}`: is the EDNS0 DO (DNSSEC OK) bit set in the query
-* `{>id}`: query ID
-* `{>opcode}`: query OPCODE
-
-The default Common Log Format is:
-
-~~~ txt
-`{remote} - [{when}] "{type} {class} {name} {proto} {size} {>do} {>bufsize}" {rcode} {>rflags} {rsize} {duration}`
-~~~
-
-## Examples
-
-Log all requests to stdout
-
-~~~
-log stdout
-~~~
-
-Custom log format, for all zones (`.`)
-
-~~~
-log . stdout "{proto} Request: {name} {type} {>id}"
-~~~
-
-Only log denials for example.org (and below to a file)
-
-~~~
-log example.org stdout {
- class denial
-}
-~~~
diff --git a/middleware/log/log.go b/middleware/log/log.go
deleted file mode 100644
index f4a976002..000000000
--- a/middleware/log/log.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// Package log implements basic but useful request (access) logging middleware.
-package log
-
-import (
- "log"
- "time"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/metrics/vars"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/pkg/rcode"
- "github.com/coredns/coredns/middleware/pkg/replacer"
- "github.com/coredns/coredns/middleware/pkg/response"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// Logger is a basic request logging middleware.
-type Logger struct {
- Next middleware.Handler
- Rules []Rule
- ErrorFunc func(dns.ResponseWriter, *dns.Msg, int) // failover error handler
-}
-
-// ServeDNS implements the middleware.Handler interface.
-func (l Logger) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- state := request.Request{W: w, Req: r}
- for _, rule := range l.Rules {
- if !middleware.Name(rule.NameScope).Matches(state.Name()) {
- continue
- }
-
- rrw := dnsrecorder.New(w)
- rc, err := middleware.NextOrFailure(l.Name(), l.Next, ctx, rrw, r)
-
- if rc > 0 {
- // There was an error up the chain, but no response has been written yet.
- // The error must be handled here so the log entry will record the response size.
- if l.ErrorFunc != nil {
- l.ErrorFunc(rrw, r, rc)
- } else {
- answer := new(dns.Msg)
- answer.SetRcode(r, rc)
- state.SizeAndDo(answer)
-
- vars.Report(state, vars.Dropped, rcode.ToString(rc), answer.Len(), time.Now())
-
- w.WriteMsg(answer)
- }
- rc = 0
- }
-
- tpe, _ := response.Typify(rrw.Msg, time.Now().UTC())
- class := response.Classify(tpe)
- if rule.Class == response.All || rule.Class == class {
- rep := replacer.New(r, rrw, CommonLogEmptyValue)
- rule.Log.Println(rep.Replace(rule.Format))
- }
-
- return rc, err
-
- }
- return middleware.NextOrFailure(l.Name(), l.Next, ctx, w, r)
-}
-
-// Name implements the Handler interface.
-func (l Logger) Name() string { return "log" }
-
-// Rule configures the logging middleware.
-type Rule struct {
- NameScope string
- Class response.Class
- OutputFile string
- Format string
- Log *log.Logger
-}
-
-const (
- // DefaultLogFilename is the default output name. This is the only supported value.
- DefaultLogFilename = "stdout"
- // CommonLogFormat is the common log format.
- CommonLogFormat = `{remote} ` + CommonLogEmptyValue + ` [{when}] "{type} {class} {name} {proto} {size} {>do} {>bufsize}" {rcode} {>rflags} {rsize} {duration}`
- // CommonLogEmptyValue is the common empty log value.
- CommonLogEmptyValue = "-"
- // CombinedLogFormat is the combined log format.
- CombinedLogFormat = CommonLogFormat + ` "{>opcode}"`
- // DefaultLogFormat is the default log format.
- DefaultLogFormat = CommonLogFormat
-)
diff --git a/middleware/log/log_test.go b/middleware/log/log_test.go
deleted file mode 100644
index 80efc971c..000000000
--- a/middleware/log/log_test.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package log
-
-import (
- "bytes"
- "log"
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/pkg/response"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func TestLoggedStatus(t *testing.T) {
- var f bytes.Buffer
- rule := Rule{
- NameScope: ".",
- Format: DefaultLogFormat,
- Log: log.New(&f, "", 0),
- }
-
- logger := Logger{
- Rules: []Rule{rule},
- Next: test.ErrorHandler(),
- }
-
- ctx := context.TODO()
- r := new(dns.Msg)
- r.SetQuestion("example.org.", dns.TypeA)
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
-
- rcode, _ := logger.ServeDNS(ctx, rec, r)
- if rcode != 0 {
- t.Errorf("Expected rcode to be 0 - was: %d", rcode)
- }
-
- logged := f.String()
- if !strings.Contains(logged, "A IN example.org. udp 29 false 512") {
- t.Errorf("Expected it to be logged. Logged string: %s", logged)
- }
-}
-
-func TestLoggedClassDenial(t *testing.T) {
- var f bytes.Buffer
- rule := Rule{
- NameScope: ".",
- Format: DefaultLogFormat,
- Log: log.New(&f, "", 0),
- Class: response.Denial,
- }
-
- logger := Logger{
- Rules: []Rule{rule},
- Next: test.ErrorHandler(),
- }
-
- ctx := context.TODO()
- r := new(dns.Msg)
- r.SetQuestion("example.org.", dns.TypeA)
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
-
- logger.ServeDNS(ctx, rec, r)
-
- logged := f.String()
- if len(logged) != 0 {
- t.Errorf("Expected it not to be logged, but got string: %s", logged)
- }
-}
-
-func TestLoggedClassError(t *testing.T) {
- var f bytes.Buffer
- rule := Rule{
- NameScope: ".",
- Format: DefaultLogFormat,
- Log: log.New(&f, "", 0),
- Class: response.Error,
- }
-
- logger := Logger{
- Rules: []Rule{rule},
- Next: test.ErrorHandler(),
- }
-
- ctx := context.TODO()
- r := new(dns.Msg)
- r.SetQuestion("example.org.", dns.TypeA)
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
-
- logger.ServeDNS(ctx, rec, r)
-
- logged := f.String()
- if !strings.Contains(logged, "SERVFAIL") {
- t.Errorf("Expected it to be logged. Logged string: %s", logged)
- }
-}
diff --git a/middleware/log/setup.go b/middleware/log/setup.go
deleted file mode 100644
index 498829888..000000000
--- a/middleware/log/setup.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package log
-
-import (
- "fmt"
- "log"
- "os"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/response"
-
- "github.com/mholt/caddy"
- "github.com/miekg/dns"
-)
-
-func init() {
- caddy.RegisterPlugin("log", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- rules, err := logParse(c)
- if err != nil {
- return middleware.Error("log", err)
- }
-
- // Open the log files for writing when the server starts
- c.OnStartup(func() error {
- for i := 0; i < len(rules); i++ {
- // We only support stdout
- writer := os.Stdout
- if rules[i].OutputFile != "stdout" {
- return middleware.Error("log", fmt.Errorf("invalid log file: %s", rules[i].OutputFile))
- }
-
- rules[i].Log = log.New(writer, "", 0)
- }
-
- return nil
- })
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- return Logger{Next: next, Rules: rules, ErrorFunc: dnsserver.DefaultErrorFunc}
- })
-
- return nil
-}
-
-func logParse(c *caddy.Controller) ([]Rule, error) {
- var rules []Rule
-
- for c.Next() {
- args := c.RemainingArgs()
-
- if len(args) == 0 {
- // Nothing specified; use defaults
- rules = append(rules, Rule{
- NameScope: ".",
- OutputFile: DefaultLogFilename,
- Format: DefaultLogFormat,
- })
- } else if len(args) == 1 {
- // Only an output file specified.
- rules = append(rules, Rule{
- NameScope: ".",
- OutputFile: args[0],
- Format: DefaultLogFormat,
- })
- } else {
- // Name scope, output file, and maybe a format specified
-
- format := DefaultLogFormat
-
- if len(args) > 2 {
- switch args[2] {
- case "{common}":
- format = CommonLogFormat
- case "{combined}":
- format = CombinedLogFormat
- default:
- format = args[2]
- }
- }
-
- rules = append(rules, Rule{
- NameScope: dns.Fqdn(args[0]),
- OutputFile: args[1],
- Format: format,
- })
- }
-
- // Class refinements in an extra block.
- for c.NextBlock() {
- switch c.Val() {
- // class followed by all, denial, error or success.
- case "class":
- classes := c.RemainingArgs()
- if len(classes) == 0 {
- return nil, c.ArgErr()
- }
- cls, err := response.ClassFromString(classes[0])
- if err != nil {
- return nil, err
- }
- // update class and the last added Rule (bit icky)
- rules[len(rules)-1].Class = cls
- default:
- return nil, c.ArgErr()
- }
- }
- }
-
- return rules, nil
-}
diff --git a/middleware/log/setup_test.go b/middleware/log/setup_test.go
deleted file mode 100644
index 596239312..000000000
--- a/middleware/log/setup_test.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package log
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/response"
-
- "github.com/mholt/caddy"
-)
-
-func TestLogParse(t *testing.T) {
- tests := []struct {
- inputLogRules string
- shouldErr bool
- expectedLogRules []Rule
- }{
- {`log`, false, []Rule{{
- NameScope: ".",
- OutputFile: DefaultLogFilename,
- Format: DefaultLogFormat,
- }}},
- {`log log.txt`, false, []Rule{{
- NameScope: ".",
- OutputFile: "log.txt",
- Format: DefaultLogFormat,
- }}},
- {`log example.org log.txt`, false, []Rule{{
- NameScope: "example.org.",
- OutputFile: "log.txt",
- Format: DefaultLogFormat,
- }}},
- {`log example.org. stdout`, false, []Rule{{
- NameScope: "example.org.",
- OutputFile: "stdout",
- Format: DefaultLogFormat,
- }}},
- {`log example.org log.txt {common}`, false, []Rule{{
- NameScope: "example.org.",
- OutputFile: "log.txt",
- Format: CommonLogFormat,
- }}},
- {`log example.org accesslog.txt {combined}`, false, []Rule{{
- NameScope: "example.org.",
- OutputFile: "accesslog.txt",
- Format: CombinedLogFormat,
- }}},
- {`log example.org. log.txt
- log example.net accesslog.txt {combined}`, false, []Rule{{
- NameScope: "example.org.",
- OutputFile: "log.txt",
- Format: DefaultLogFormat,
- }, {
- NameScope: "example.net.",
- OutputFile: "accesslog.txt",
- Format: CombinedLogFormat,
- }}},
- {`log example.org stdout {host}
- log example.org log.txt {when}`, false, []Rule{{
- NameScope: "example.org.",
- OutputFile: "stdout",
- Format: "{host}",
- }, {
- NameScope: "example.org.",
- OutputFile: "log.txt",
- Format: "{when}",
- }}},
-
- {`log example.org log.txt {
- class all
- }`, false, []Rule{{
- NameScope: "example.org.",
- OutputFile: "log.txt",
- Format: CommonLogFormat,
- Class: response.All,
- }}},
- {`log example.org log.txt {
- class denial
- }`, false, []Rule{{
- NameScope: "example.org.",
- OutputFile: "log.txt",
- Format: CommonLogFormat,
- Class: response.Denial,
- }}},
- {`log {
- class denial
- }`, false, []Rule{{
- NameScope: ".",
- OutputFile: DefaultLogFilename,
- Format: CommonLogFormat,
- Class: response.Denial,
- }}},
- }
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.inputLogRules)
- actualLogRules, err := logParse(c)
-
- if err == nil && test.shouldErr {
- t.Errorf("Test %d didn't error, but it should have", i)
- } else if err != nil && !test.shouldErr {
- t.Errorf("Test %d errored, but it shouldn't have; got '%v'", i, err)
- }
- if len(actualLogRules) != len(test.expectedLogRules) {
- t.Fatalf("Test %d expected %d no of Log rules, but got %d ",
- i, len(test.expectedLogRules), len(actualLogRules))
- }
- for j, actualLogRule := range actualLogRules {
-
- if actualLogRule.NameScope != test.expectedLogRules[j].NameScope {
- t.Errorf("Test %d expected %dth LogRule NameScope to be %s , but got %s",
- i, j, test.expectedLogRules[j].NameScope, actualLogRule.NameScope)
- }
-
- if actualLogRule.OutputFile != test.expectedLogRules[j].OutputFile {
- t.Errorf("Test %d expected %dth LogRule OutputFile to be %s , but got %s",
- i, j, test.expectedLogRules[j].OutputFile, actualLogRule.OutputFile)
- }
-
- if actualLogRule.Format != test.expectedLogRules[j].Format {
- t.Errorf("Test %d expected %dth LogRule Format to be %s , but got %s",
- i, j, test.expectedLogRules[j].Format, actualLogRule.Format)
- }
-
- if actualLogRule.Class != test.expectedLogRules[j].Class {
- t.Errorf("Test %d expected %dth LogRule Class to be %s , but got %s",
- i, j, test.expectedLogRules[j].Class, actualLogRule.Class)
- }
- }
- }
-
-}
diff --git a/middleware/metrics/README.md b/middleware/metrics/README.md
deleted file mode 100644
index a1ab52b88..000000000
--- a/middleware/metrics/README.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# prometheus
-
-This module enables prometheus metrics for CoreDNS.
-
-The default location for the metrics is `localhost:9153`. The metrics path is fixed to `/metrics`.
-The following metrics are exported:
-
-* coredns_dns_request_count_total{zone, proto, family}
-* coredns_dns_request_duration_milliseconds{zone}
-* coredns_dns_request_size_bytes{zone, proto}
-* coredns_dns_request_do_count_total{zone}
-* coredns_dns_request_type_count_total{zone, type}
-* coredns_dns_response_size_bytes{zone, proto}
-* coredns_dns_response_rcode_count_total{zone, rcode}
-
-Each counter has a label `zone` which is the zonename used for the request/response.
-
-Extra labels used are:
-
-* `proto` which holds the transport of the response ("udp" or "tcp")
-* The address family (`family`) of the transport (1 = IP (IP version 4), 2 = IP6 (IP version 6)).
-* `type` which holds the query type. It holds most common types (A, AAAA, MX, SOA, CNAME, PTR, TXT,
- NS, SRV, DS, DNSKEY, RRSIG, NSEC, NSEC3, IXFR, AXFR and ANY) and "other" which lumps together all
- other types.
-* The `response_rcode_count_total` has an extra label `rcode` which holds the rcode of the response.
-
-If monitoring is enabled, queries that do not enter the middleware chain are exported under the fake
-name "dropped" (without a closing dot - this is never a valid domain name).
-
-
-## Syntax
-
-~~~
-prometheus [ADDRESS]
-~~~
-
-For each zone that you want to see metrics for.
-
-It optionally takes an address to which the metrics are exported; the default
-is `localhost:9153`. The metrics path is fixed to `/metrics`.
-
-## Examples
-
-Use an alternative address:
-
-~~~
-prometheus localhost:9253
-~~~
-
-# Bugs
-
-When reloading, we keep the handler running, meaning that any changes to the handler's address
-aren't picked up. You'll need to restart CoreDNS for that to happen.
diff --git a/middleware/metrics/handler.go b/middleware/metrics/handler.go
deleted file mode 100644
index 50dc4141e..000000000
--- a/middleware/metrics/handler.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package metrics
-
-import (
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/metrics/vars"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/pkg/rcode"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// ServeDNS implements the Handler interface.
-func (m *Metrics) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- state := request.Request{W: w, Req: r}
-
- qname := state.QName()
- zone := middleware.Zones(m.ZoneNames()).Matches(qname)
- if zone == "" {
- zone = "."
- }
-
- // Record response to get status code and size of the reply.
- rw := dnsrecorder.New(w)
- status, err := middleware.NextOrFailure(m.Name(), m.Next, ctx, rw, r)
-
- vars.Report(state, zone, rcode.ToString(rw.Rcode), rw.Len, rw.Start)
-
- return status, err
-}
-
-// Name implements the Handler interface.
-func (m *Metrics) Name() string { return "prometheus" }
diff --git a/middleware/metrics/metrics.go b/middleware/metrics/metrics.go
deleted file mode 100644
index 6e06a2bf4..000000000
--- a/middleware/metrics/metrics.go
+++ /dev/null
@@ -1,101 +0,0 @@
-// Package metrics implement a handler and middleware that provides Prometheus metrics.
-package metrics
-
-import (
- "log"
- "net"
- "net/http"
- "sync"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/metrics/vars"
-
- "github.com/prometheus/client_golang/prometheus"
-)
-
-func init() {
- prometheus.MustRegister(vars.RequestCount)
- prometheus.MustRegister(vars.RequestDuration)
- prometheus.MustRegister(vars.RequestSize)
- prometheus.MustRegister(vars.RequestDo)
- prometheus.MustRegister(vars.RequestType)
-
- prometheus.MustRegister(vars.ResponseSize)
- prometheus.MustRegister(vars.ResponseRcode)
-}
-
-// Metrics holds the prometheus configuration. The metrics' path is fixed to be /metrics
-type Metrics struct {
- Next middleware.Handler
- Addr string
- ln net.Listener
- mux *http.ServeMux
-
- zoneNames []string
- zoneMap map[string]bool
- zoneMu sync.RWMutex
-}
-
-// AddZone adds zone z to m.
-func (m *Metrics) AddZone(z string) {
- m.zoneMu.Lock()
- m.zoneMap[z] = true
- m.zoneNames = keys(m.zoneMap)
- m.zoneMu.Unlock()
-}
-
-// RemoveZone remove zone z from m.
-func (m *Metrics) RemoveZone(z string) {
- m.zoneMu.Lock()
- delete(m.zoneMap, z)
- m.zoneNames = keys(m.zoneMap)
- m.zoneMu.Unlock()
-}
-
-// ZoneNames returns the zones of m.
-func (m *Metrics) ZoneNames() []string {
- m.zoneMu.RLock()
- s := m.zoneNames
- m.zoneMu.RUnlock()
- return s
-}
-
-// OnStartup sets up the metrics on startup.
-func (m *Metrics) OnStartup() error {
- ln, err := net.Listen("tcp", m.Addr)
- if err != nil {
- log.Printf("[ERROR] Failed to start metrics handler: %s", err)
- return err
- }
-
- m.ln = ln
- ListenAddr = m.ln.Addr().String()
-
- m.mux = http.NewServeMux()
- m.mux.Handle("/metrics", prometheus.Handler())
-
- go func() {
- http.Serve(m.ln, m.mux)
- }()
- return nil
-}
-
-// OnShutdown tears down the metrics on shutdown and restart.
-func (m *Metrics) OnShutdown() error {
- if m.ln != nil {
- return m.ln.Close()
- }
- return nil
-}
-
-func keys(m map[string]bool) []string {
- sx := []string{}
- for k := range m {
- sx = append(sx, k)
- }
- return sx
-}
-
-// ListenAddr is assigned the address of the prometheus listener. Its use is mainly in tests where
-// we listen on "localhost:0" and need to retrieve the actual address.
-var ListenAddr string
diff --git a/middleware/metrics/metrics_test.go b/middleware/metrics/metrics_test.go
deleted file mode 100644
index 18a9011ed..000000000
--- a/middleware/metrics/metrics_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package metrics
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware"
- mtest "github.com/coredns/coredns/middleware/metrics/test"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func TestMetrics(t *testing.T) {
- met := &Metrics{Addr: "localhost:0", zoneMap: make(map[string]bool)}
- if err := met.OnStartup(); err != nil {
- t.Fatalf("Failed to start metrics handler: %s", err)
- }
- defer met.OnShutdown()
-
- met.AddZone("example.org.")
-
- tests := []struct {
- next middleware.Handler
- qname string
- qtype uint16
- metric string
- expectedValue string
- }{
- // This all works because 1 bucket (1 zone, 1 type)
- {
- next: test.NextHandler(dns.RcodeSuccess, nil),
- qname: "example.org",
- metric: "coredns_dns_request_count_total",
- expectedValue: "1",
- },
- {
- next: test.NextHandler(dns.RcodeSuccess, nil),
- qname: "example.org",
- metric: "coredns_dns_request_count_total",
- expectedValue: "2",
- },
- {
- next: test.NextHandler(dns.RcodeSuccess, nil),
- qname: "example.org",
- metric: "coredns_dns_request_type_count_total",
- expectedValue: "3",
- },
- {
- next: test.NextHandler(dns.RcodeSuccess, nil),
- qname: "example.org",
- metric: "coredns_dns_response_rcode_count_total",
- expectedValue: "4",
- },
- }
-
- ctx := context.TODO()
-
- for i, tc := range tests {
- req := new(dns.Msg)
- if tc.qtype == 0 {
- tc.qtype = dns.TypeA
- }
- req.SetQuestion(dns.Fqdn(tc.qname), tc.qtype)
- met.Next = tc.next
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- _, err := met.ServeDNS(ctx, rec, req)
- if err != nil {
- t.Fatalf("Test %d: Expected no error, but got %s", i, err)
- }
-
- result := mtest.Scrape(t, "http://"+ListenAddr+"/metrics")
-
- if tc.expectedValue != "" {
- got, _ := mtest.MetricValue(tc.metric, result)
- if got != tc.expectedValue {
- t.Errorf("Test %d: Expected value %s for metrics %s, but got %s", i, tc.expectedValue, tc.metric, got)
- }
- }
- }
-}
diff --git a/middleware/metrics/setup.go b/middleware/metrics/setup.go
deleted file mode 100644
index 30b05d4ff..000000000
--- a/middleware/metrics/setup.go
+++ /dev/null
@@ -1,100 +0,0 @@
-package metrics
-
-import (
- "net"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("prometheus", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-
- uniqAddr = addrs{a: make(map[string]int)}
-}
-
-func setup(c *caddy.Controller) error {
- m, err := prometheusParse(c)
- if err != nil {
- return middleware.Error("prometheus", err)
- }
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- m.Next = next
- return m
- })
-
- for a, v := range uniqAddr.a {
- if v == todo {
- // During restarts we will keep this handler running, BUG.
- c.OncePerServerBlock(m.OnStartup)
- }
- uniqAddr.a[a] = done
- }
- c.OnFinalShutdown(m.OnShutdown)
-
- return nil
-}
-
-func prometheusParse(c *caddy.Controller) (*Metrics, error) {
- var (
- met = &Metrics{Addr: addr, zoneMap: make(map[string]bool)}
- err error
- )
-
- defer func() {
- uniqAddr.SetAddress(met.Addr)
- }()
-
- for c.Next() {
- if len(met.ZoneNames()) > 0 {
- return met, c.Err("can only have one metrics module per server")
- }
-
- for _, z := range c.ServerBlockKeys {
- met.AddZone(middleware.Host(z).Normalize())
- }
- args := c.RemainingArgs()
-
- switch len(args) {
- case 0:
- case 1:
- met.Addr = args[0]
- _, _, e := net.SplitHostPort(met.Addr)
- if e != nil {
- return met, e
- }
- default:
- return met, c.ArgErr()
- }
- }
- return met, err
-}
-
-var uniqAddr addrs
-
-// Keep track on which addrs we listen, so we only start one listener.
-type addrs struct {
- a map[string]int
-}
-
-func (a *addrs) SetAddress(addr string) {
- // If already there and set to done, we've already started this listener.
- if a.a[addr] == done {
- return
- }
- a.a[addr] = todo
-}
-
-// Addr is the address the where the metrics are exported by default.
-const addr = "localhost:9153"
-
-const (
- todo = 1
- done = 2
-)
diff --git a/middleware/metrics/setup_test.go b/middleware/metrics/setup_test.go
deleted file mode 100644
index 73555427e..000000000
--- a/middleware/metrics/setup_test.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package metrics
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestPrometheusParse(t *testing.T) {
- tests := []struct {
- input string
- shouldErr bool
- addr string
- }{
- // oks
- {`prometheus`, false, "localhost:9153"},
- {`prometheus localhost:53`, false, "localhost:53"},
- // fails
- {`prometheus {}`, true, ""},
- {`prometheus /foo`, true, ""},
- {`prometheus a b c`, true, ""},
- }
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- m, err := prometheusParse(c)
- if test.shouldErr && err == nil {
- t.Errorf("Test %v: Expected error but found nil", i)
- continue
- } else if !test.shouldErr && err != nil {
- t.Errorf("Test %v: Expected no error but found error: %v", i, err)
- continue
- }
-
- if test.shouldErr {
- continue
- }
-
- if test.addr != m.Addr {
- t.Errorf("Test %v: Expected address %s but found: %s", i, test.addr, m.Addr)
- }
- }
-}
diff --git a/middleware/metrics/test/scrape.go b/middleware/metrics/test/scrape.go
deleted file mode 100644
index a21c0061d..000000000
--- a/middleware/metrics/test/scrape.go
+++ /dev/null
@@ -1,225 +0,0 @@
-// Adapted by Miek Gieben for CoreDNS testing.
-//
-// License from prom2json
-// Copyright 2014 Prometheus Team
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package test will scrape a target and you can inspect the variables.
-// Basic usage:
-//
-// result := Scrape("http://localhost:9153/metrics")
-// v := MetricValue("coredns_cache_capacity", result)
-//
-package test
-
-import (
- "fmt"
- "io"
- "mime"
- "net/http"
- "testing"
-
- "github.com/matttproud/golang_protobuf_extensions/pbutil"
- "github.com/prometheus/common/expfmt"
-
- dto "github.com/prometheus/client_model/go"
-)
-
-type (
- // MetricFamily holds a prometheus metric.
- MetricFamily struct {
- Name string `json:"name"`
- Help string `json:"help"`
- Type string `json:"type"`
- Metrics []interface{} `json:"metrics,omitempty"` // Either metric or summary.
- }
-
- // metric is for all "single value" metrics.
- metric struct {
- Labels map[string]string `json:"labels,omitempty"`
- Value string `json:"value"`
- }
-
- summary struct {
- Labels map[string]string `json:"labels,omitempty"`
- Quantiles map[string]string `json:"quantiles,omitempty"`
- Count string `json:"count"`
- Sum string `json:"sum"`
- }
-
- histogram struct {
- Labels map[string]string `json:"labels,omitempty"`
- Buckets map[string]string `json:"buckets,omitempty"`
- Count string `json:"count"`
- Sum string `json:"sum"`
- }
-)
-
-// Scrape returns the all the vars a []*metricFamily.
-func Scrape(t *testing.T, url string) []*MetricFamily {
- mfChan := make(chan *dto.MetricFamily, 1024)
-
- go fetchMetricFamilies(url, mfChan)
-
- result := []*MetricFamily{}
- for mf := range mfChan {
- result = append(result, newMetricFamily(mf))
- }
- return result
-}
-
-// MetricValue returns the value associated with name as a string as well as the labels.
-// It only returns the first metrics of the slice.
-func MetricValue(name string, mfs []*MetricFamily) (string, map[string]string) {
- for _, mf := range mfs {
- if mf.Name == name {
- // Only works with Gauge and Counter...
- return mf.Metrics[0].(metric).Value, mf.Metrics[0].(metric).Labels
- }
- }
- return "", nil
-}
-
-// MetricValueLabel returns the value for name *and* label *value*.
-func MetricValueLabel(name, label string, mfs []*MetricFamily) (string, map[string]string) {
- // bit hacky is this really handy...?
- for _, mf := range mfs {
- if mf.Name == name {
- for _, m := range mf.Metrics {
- for _, v := range m.(metric).Labels {
- if v == label {
- return m.(metric).Value, m.(metric).Labels
- }
- }
-
- }
- }
- }
- return "", nil
-}
-
-func newMetricFamily(dtoMF *dto.MetricFamily) *MetricFamily {
- mf := &MetricFamily{
- Name: dtoMF.GetName(),
- Help: dtoMF.GetHelp(),
- Type: dtoMF.GetType().String(),
- Metrics: make([]interface{}, len(dtoMF.Metric)),
- }
- for i, m := range dtoMF.Metric {
- if dtoMF.GetType() == dto.MetricType_SUMMARY {
- mf.Metrics[i] = summary{
- Labels: makeLabels(m),
- Quantiles: makeQuantiles(m),
- Count: fmt.Sprint(m.GetSummary().GetSampleCount()),
- Sum: fmt.Sprint(m.GetSummary().GetSampleSum()),
- }
- } else if dtoMF.GetType() == dto.MetricType_HISTOGRAM {
- mf.Metrics[i] = histogram{
- Labels: makeLabels(m),
- Buckets: makeBuckets(m),
- Count: fmt.Sprint(m.GetHistogram().GetSampleCount()),
- Sum: fmt.Sprint(m.GetSummary().GetSampleSum()),
- }
- } else {
- mf.Metrics[i] = metric{
- Labels: makeLabels(m),
- Value: fmt.Sprint(value(m)),
- }
- }
- }
- return mf
-}
-
-func value(m *dto.Metric) float64 {
- if m.Gauge != nil {
- return m.GetGauge().GetValue()
- }
- if m.Counter != nil {
- return m.GetCounter().GetValue()
- }
- if m.Untyped != nil {
- return m.GetUntyped().GetValue()
- }
- return 0.
-}
-
-func makeLabels(m *dto.Metric) map[string]string {
- result := map[string]string{}
- for _, lp := range m.Label {
- result[lp.GetName()] = lp.GetValue()
- }
- return result
-}
-
-func makeQuantiles(m *dto.Metric) map[string]string {
- result := map[string]string{}
- for _, q := range m.GetSummary().Quantile {
- result[fmt.Sprint(q.GetQuantile())] = fmt.Sprint(q.GetValue())
- }
- return result
-}
-
-func makeBuckets(m *dto.Metric) map[string]string {
- result := map[string]string{}
- for _, b := range m.GetHistogram().Bucket {
- result[fmt.Sprint(b.GetUpperBound())] = fmt.Sprint(b.GetCumulativeCount())
- }
- return result
-}
-
-func fetchMetricFamilies(url string, ch chan<- *dto.MetricFamily) {
- defer close(ch)
- req, err := http.NewRequest("GET", url, nil)
- if err != nil {
- return
- }
- req.Header.Add("Accept", acceptHeader)
- resp, err := http.DefaultClient.Do(req)
- if err != nil {
- return
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- return
- }
-
- mediatype, params, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
- if err == nil && mediatype == "application/vnd.google.protobuf" &&
- params["encoding"] == "delimited" &&
- params["proto"] == "io.prometheus.client.MetricFamily" {
- for {
- mf := &dto.MetricFamily{}
- if _, err = pbutil.ReadDelimited(resp.Body, mf); err != nil {
- if err == io.EOF {
- break
- }
- return
- }
- ch <- mf
- }
- } else {
- // We could do further content-type checks here, but the
- // fallback for now will anyway be the text format
- // version 0.0.4, so just go for it and see if it works.
- var parser expfmt.TextParser
- metricFamilies, err := parser.TextToMetricFamilies(resp.Body)
- if err != nil {
- return
- }
- for _, mf := range metricFamilies {
- ch <- mf
- }
- }
-}
-
-const acceptHeader = `application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.7,text/plain;version=0.0.4;q=0.3`
diff --git a/middleware/metrics/vars/report.go b/middleware/metrics/vars/report.go
deleted file mode 100644
index 5d8f2ba64..000000000
--- a/middleware/metrics/vars/report.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package vars
-
-import (
- "time"
-
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-// Report reports the metrics data associcated with request.
-func Report(req request.Request, zone, rcode string, size int, start time.Time) {
- // Proto and Family.
- net := req.Proto()
- fam := "1"
- if req.Family() == 2 {
- fam = "2"
- }
-
- typ := req.QType()
-
- RequestCount.WithLabelValues(zone, net, fam).Inc()
- RequestDuration.WithLabelValues(zone).Observe(float64(time.Since(start) / time.Millisecond))
-
- if req.Do() {
- RequestDo.WithLabelValues(zone).Inc()
- }
-
- if _, known := monitorType[typ]; known {
- RequestType.WithLabelValues(zone, dns.Type(typ).String()).Inc()
- } else {
- RequestType.WithLabelValues(zone, other).Inc()
- }
-
- ResponseSize.WithLabelValues(zone, net).Observe(float64(size))
- RequestSize.WithLabelValues(zone, net).Observe(float64(req.Len()))
-
- ResponseRcode.WithLabelValues(zone, rcode).Inc()
-}
-
-var monitorType = map[uint16]bool{
- dns.TypeAAAA: true,
- dns.TypeA: true,
- dns.TypeCNAME: true,
- dns.TypeDNSKEY: true,
- dns.TypeDS: true,
- dns.TypeMX: true,
- dns.TypeNSEC3: true,
- dns.TypeNSEC: true,
- dns.TypeNS: true,
- dns.TypePTR: true,
- dns.TypeRRSIG: true,
- dns.TypeSOA: true,
- dns.TypeSRV: true,
- dns.TypeTXT: true,
- // Meta Qtypes
- dns.TypeIXFR: true,
- dns.TypeAXFR: true,
- dns.TypeANY: true,
-}
-
-const other = "other"
diff --git a/middleware/metrics/vars/vars.go b/middleware/metrics/vars/vars.go
deleted file mode 100644
index 42af10007..000000000
--- a/middleware/metrics/vars/vars.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package vars
-
-import (
- "github.com/coredns/coredns/middleware"
-
- "github.com/prometheus/client_golang/prometheus"
-)
-
-// Request* and Response* are the prometheus counters and gauges we are using for exporting metrics.
-var (
- RequestCount = prometheus.NewCounterVec(prometheus.CounterOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "request_count_total",
- Help: "Counter of DNS requests made per zone, protocol and family.",
- }, []string{"zone", "proto", "family"})
-
- RequestDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "request_duration_milliseconds",
- Buckets: append(prometheus.DefBuckets, []float64{50, 100, 200, 500, 1000, 2000, 3000, 4000, 5000, 10000}...),
- Help: "Histogram of the time (in milliseconds) each request took.",
- }, []string{"zone"})
-
- RequestSize = prometheus.NewHistogramVec(prometheus.HistogramOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "request_size_bytes",
- Help: "Size of the EDNS0 UDP buffer in bytes (64K for TCP).",
- Buckets: []float64{0, 100, 200, 300, 400, 511, 1023, 2047, 4095, 8291, 16e3, 32e3, 48e3, 64e3},
- }, []string{"zone", "proto"})
-
- RequestDo = prometheus.NewCounterVec(prometheus.CounterOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "request_do_count_total",
- Help: "Counter of DNS requests with DO bit set per zone.",
- }, []string{"zone"})
-
- RequestType = prometheus.NewCounterVec(prometheus.CounterOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "request_type_count_total",
- Help: "Counter of DNS requests per type, per zone.",
- }, []string{"zone", "type"})
-
- ResponseSize = prometheus.NewHistogramVec(prometheus.HistogramOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "response_size_bytes",
- Help: "Size of the returned response in bytes.",
- Buckets: []float64{0, 100, 200, 300, 400, 511, 1023, 2047, 4095, 8291, 16e3, 32e3, 48e3, 64e3},
- }, []string{"zone", "proto"})
-
- ResponseRcode = prometheus.NewCounterVec(prometheus.CounterOpts{
- Namespace: middleware.Namespace,
- Subsystem: subsystem,
- Name: "response_rcode_count_total",
- Help: "Counter of response status codes.",
- }, []string{"zone", "rcode"})
-)
-
-const (
- subsystem = "dns"
-
- // Dropped indicates we dropped the query before any handling. It has no closing dot, so it can not be a valid zone.
- Dropped = "dropped"
-)
diff --git a/middleware/middleware.go b/middleware/middleware.go
deleted file mode 100644
index 9e236c6e0..000000000
--- a/middleware/middleware.go
+++ /dev/null
@@ -1,102 +0,0 @@
-// Package middleware provides some types and functions common among middleware.
-package middleware
-
-import (
- "errors"
- "fmt"
-
- "github.com/miekg/dns"
- ot "github.com/opentracing/opentracing-go"
- "golang.org/x/net/context"
-)
-
-type (
- // Middleware is the middle layer which represents the traditional
- // idea of middleware: it chains one Handler to the next by being
- // passed the next Handler in the chain.
- Middleware func(Handler) Handler
-
- // Handler is like dns.Handler except ServeDNS may return an rcode
- // and/or error.
- //
- // If ServeDNS writes to the response body, it should return a status
- // code. If the status code is not one of the following:
- //
- // * SERVFAIL (dns.RcodeServerFailure)
- //
- // * REFUSED (dns.RecodeRefused)
- //
- // * FORMERR (dns.RcodeFormatError)
- //
- // * NOTIMP (dns.RcodeNotImplemented)
- //
- // CoreDNS assumes *no* reply has yet been written. All other response
- // codes signal other handlers above it that the response message is
- // already written, and that they should not write to it also.
- //
- // If ServeDNS encounters an error, it should return the error value
- // so it can be logged by designated error-handling middleware.
- //
- // If writing a response after calling another ServeDNS method, the
- // returned rcode SHOULD be used when writing the response.
- //
- // If handling errors after calling another ServeDNS method, the
- // returned error value SHOULD be logged or handled accordingly.
- //
- // Otherwise, return values should be propagated down the middleware
- // chain by returning them unchanged.
- Handler interface {
- ServeDNS(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
- Name() string
- }
-
- // HandlerFunc is a convenience type like dns.HandlerFunc, except
- // ServeDNS returns an rcode and an error. See Handler
- // documentation for more information.
- HandlerFunc func(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
-)
-
-// ServeDNS implements the Handler interface.
-func (f HandlerFunc) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- return f(ctx, w, r)
-}
-
-// Name implements the Handler interface.
-func (f HandlerFunc) Name() string { return "handlerfunc" }
-
-// Error returns err with 'middleware/name: ' prefixed to it.
-func Error(name string, err error) error { return fmt.Errorf("%s/%s: %s", "middleware", name, err) }
-
-// NextOrFailure calls next.ServeDNS when next is not nill, otherwise it will return, a ServerFailure
-// and a nil error.
-func NextOrFailure(name string, next Handler, ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- if next != nil {
- if span := ot.SpanFromContext(ctx); span != nil {
- child := span.Tracer().StartSpan(next.Name(), ot.ChildOf(span.Context()))
- defer child.Finish()
- ctx = ot.ContextWithSpan(ctx, child)
- }
- return next.ServeDNS(ctx, w, r)
- }
-
- return dns.RcodeServerFailure, Error(name, errors.New("no next middleware found"))
-}
-
-// ClientWrite returns true if the response has been written to the client.
-// Each middleware to adhire to this protocol.
-func ClientWrite(rcode int) bool {
- switch rcode {
- case dns.RcodeServerFailure:
- fallthrough
- case dns.RcodeRefused:
- fallthrough
- case dns.RcodeFormatError:
- fallthrough
- case dns.RcodeNotImplemented:
- return false
- }
- return true
-}
-
-// Namespace is the namespace used for the metrics.
-const Namespace = "coredns"
diff --git a/middleware/middleware_test.go b/middleware/middleware_test.go
deleted file mode 100644
index c870d7c16..000000000
--- a/middleware/middleware_test.go
+++ /dev/null
@@ -1 +0,0 @@
-package middleware
diff --git a/middleware/normalize.go b/middleware/normalize.go
deleted file mode 100644
index 92cc57937..000000000
--- a/middleware/normalize.go
+++ /dev/null
@@ -1,137 +0,0 @@
-package middleware
-
-import (
- "fmt"
- "net"
- "strconv"
- "strings"
-
- "github.com/miekg/dns"
-)
-
-// See core/dnsserver/address.go - we should unify these two impls.
-
-// Zones respresents a lists of zone names.
-type Zones []string
-
-// Matches checks is qname is a subdomain of any of the zones in z. The match
-// will return the most specific zones that matches other. The empty string
-// signals a not found condition.
-func (z Zones) Matches(qname string) string {
- zone := ""
- for _, zname := range z {
- if dns.IsSubDomain(zname, qname) {
- // We want the *longest* matching zone, otherwise we may end up in a parent
- if len(zname) > len(zone) {
- zone = zname
- }
- }
- }
- return zone
-}
-
-// Normalize fully qualifies all zones in z. The zones in Z must be domain names, without
-// a port or protocol prefix.
-func (z Zones) Normalize() {
- for i := range z {
- z[i] = Name(z[i]).Normalize()
- }
-}
-
-// Name represents a domain name.
-type Name string
-
-// Matches checks to see if other is a subdomain (or the same domain) of n.
-// This method assures that names can be easily and consistently matched.
-func (n Name) Matches(child string) bool {
- if dns.Name(n) == dns.Name(child) {
- return true
- }
- return dns.IsSubDomain(string(n), child)
-}
-
-// Normalize lowercases and makes n fully qualified.
-func (n Name) Normalize() string { return strings.ToLower(dns.Fqdn(string(n))) }
-
-type (
- // Host represents a host from the Corefile, may contain port.
- Host string
-)
-
-// Normalize will return the host portion of host, stripping
-// of any port or transport. The host will also be fully qualified and lowercased.
-func (h Host) Normalize() string {
-
- s := string(h)
-
- switch {
- case strings.HasPrefix(s, TransportTLS+"://"):
- s = s[len(TransportTLS+"://"):]
- case strings.HasPrefix(s, TransportDNS+"://"):
- s = s[len(TransportDNS+"://"):]
- case strings.HasPrefix(s, TransportGRPC+"://"):
- s = s[len(TransportGRPC+"://"):]
- }
-
- // The error can be ignore here, because this function is called after the corefile
- // has already been vetted.
- host, _, _ := SplitHostPort(s)
- return Name(host).Normalize()
-}
-
-// SplitHostPort splits s up in a host and port portion, taking reverse address notation into account.
-// String the string s should *not* be prefixed with any protocols, i.e. dns://
-func SplitHostPort(s string) (host, port string, err error) {
- // If there is: :[0-9]+ on the end we assume this is the port. This works for (ascii) domain
- // names and our reverse syntax, which always needs a /mask *before* the port.
- // So from the back, find first colon, and then check if its a number.
- host = s
-
- colon := strings.LastIndex(s, ":")
- if colon == len(s)-1 {
- return "", "", fmt.Errorf("expecting data after last colon: %q", s)
- }
- if colon != -1 {
- if p, err := strconv.Atoi(s[colon+1:]); err == nil {
- port = strconv.Itoa(p)
- host = s[:colon]
- }
- }
-
- // TODO(miek): this should take escaping into account.
- if len(host) > 255 {
- return "", "", fmt.Errorf("specified zone is too long: %d > 255", len(host))
- }
-
- _, d := dns.IsDomainName(host)
- if !d {
- return "", "", fmt.Errorf("zone is not a valid domain name: %s", host)
- }
-
- // Check if it parses as a reverse zone, if so we use that. Must be fully
- // specified IP and mask and mask % 8 = 0.
- ip, net, err := net.ParseCIDR(host)
- if err == nil {
- if rev, e := dns.ReverseAddr(ip.String()); e == nil {
- ones, bits := net.Mask.Size()
- if (bits-ones)%8 == 0 {
- offset, end := 0, false
- for i := 0; i < (bits-ones)/8; i++ {
- offset, end = dns.NextLabel(rev, offset)
- if end {
- break
- }
- }
- host = rev[offset:]
- }
- }
- }
- return host, port, nil
-}
-
-// Duplicated from core/dnsserver/address.go !
-const (
- TransportDNS = "dns"
- TransportTLS = "tls"
- TransportGRPC = "grpc"
-)
diff --git a/middleware/normalize_test.go b/middleware/normalize_test.go
deleted file mode 100644
index e3d2268d3..000000000
--- a/middleware/normalize_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package middleware
-
-import "testing"
-
-func TestZoneMatches(t *testing.T) {
- child := "example.org."
- zones := Zones([]string{"org.", "."})
- actual := zones.Matches(child)
- if actual != "org." {
- t.Errorf("Expected %v, got %v", "org.", actual)
- }
-
- child = "bla.example.org."
- zones = Zones([]string{"bla.example.org.", "org.", "."})
- actual = zones.Matches(child)
-
- if actual != "bla.example.org." {
- t.Errorf("Expected %v, got %v", "org.", actual)
- }
-}
-
-func TestZoneNormalize(t *testing.T) {
- zones := Zones([]string{"example.org", "Example.ORG.", "example.org."})
- expected := "example.org."
- zones.Normalize()
-
- for _, actual := range zones {
- if actual != expected {
- t.Errorf("Expected %v, got %v\n", expected, actual)
- }
- }
-}
-
-func TestNameMatches(t *testing.T) {
- matches := []struct {
- child string
- parent string
- expected bool
- }{
- {".", ".", true},
- {"example.org.", ".", true},
- {"example.org.", "example.org.", true},
- {"example.org.", "org.", true},
- {"org.", "example.org.", false},
- }
-
- for _, m := range matches {
- actual := Name(m.parent).Matches(m.child)
- if actual != m.expected {
- t.Errorf("Expected %v for %s/%s, got %v", m.expected, m.parent, m.child, actual)
- }
-
- }
-}
-
-func TestNameNormalize(t *testing.T) {
- names := []string{
- "example.org", "example.org.",
- "Example.ORG.", "example.org."}
-
- for i := 0; i < len(names); i += 2 {
- ts := names[i]
- expected := names[i+1]
- actual := Name(ts).Normalize()
- if expected != actual {
- t.Errorf("Expected %v, got %v\n", expected, actual)
- }
- }
-}
-
-func TestHostNormalize(t *testing.T) {
- hosts := []string{".:53", ".", "example.org:53", "example.org.", "example.org.:53", "example.org.",
- "10.0.0.0/8:53", "10.in-addr.arpa.", "10.0.0.0/9", "10.0.0.0/9.",
- "dns://example.org", "example.org."}
-
- for i := 0; i < len(hosts); i += 2 {
- ts := hosts[i]
- expected := hosts[i+1]
- actual := Host(ts).Normalize()
- if expected != actual {
- t.Errorf("Expected %v, got %v\n", expected, actual)
- }
- }
-}
diff --git a/middleware/pkg/cache/cache.go b/middleware/pkg/cache/cache.go
deleted file mode 100644
index 56cae2180..000000000
--- a/middleware/pkg/cache/cache.go
+++ /dev/null
@@ -1,129 +0,0 @@
-// Package cache implements a cache. The cache hold 256 shards, each shard
-// holds a cache: a map with a mutex. There is no fancy expunge algorithm, it
-// just randomly evicts elements when it gets full.
-package cache
-
-import (
- "hash/fnv"
- "sync"
-)
-
-// Hash returns the FNV hash of what.
-func Hash(what []byte) uint32 {
- h := fnv.New32()
- h.Write(what)
- return h.Sum32()
-}
-
-// Cache is cache.
-type Cache struct {
- shards [shardSize]*shard
-}
-
-// shard is a cache with random eviction.
-type shard struct {
- items map[uint32]interface{}
- size int
-
- sync.RWMutex
-}
-
-// New returns a new cache.
-func New(size int) *Cache {
- ssize := size / shardSize
- if ssize < 512 {
- ssize = 512
- }
-
- c := &Cache{}
-
- // Initialize all the shards
- for i := 0; i < shardSize; i++ {
- c.shards[i] = newShard(ssize)
- }
- return c
-}
-
-// Add adds a new element to the cache. If the element already exists it is overwritten.
-func (c *Cache) Add(key uint32, el interface{}) {
- shard := key & (shardSize - 1)
- c.shards[shard].Add(key, el)
-}
-
-// Get looks up element index under key.
-func (c *Cache) Get(key uint32) (interface{}, bool) {
- shard := key & (shardSize - 1)
- return c.shards[shard].Get(key)
-}
-
-// Remove removes the element indexed with key.
-func (c *Cache) Remove(key uint32) {
- shard := key & (shardSize - 1)
- c.shards[shard].Remove(key)
-}
-
-// Len returns the number of elements in the cache.
-func (c *Cache) Len() int {
- l := 0
- for _, s := range c.shards {
- l += s.Len()
- }
- return l
-}
-
-// newShard returns a new shard with size.
-func newShard(size int) *shard { return &shard{items: make(map[uint32]interface{}), size: size} }
-
-// Add adds element indexed by key into the cache. Any existing element is overwritten
-func (s *shard) Add(key uint32, el interface{}) {
- l := s.Len()
- if l+1 > s.size {
- s.Evict()
- }
-
- s.Lock()
- s.items[key] = el
- s.Unlock()
-}
-
-// Remove removes the element indexed by key from the cache.
-func (s *shard) Remove(key uint32) {
- s.Lock()
- delete(s.items, key)
- s.Unlock()
-}
-
-// Evict removes a random element from the cache.
-func (s *shard) Evict() {
- s.Lock()
- defer s.Unlock()
-
- key := -1
- for k := range s.items {
- key = int(k)
- break
- }
- if key == -1 {
- // empty cache
- return
- }
- delete(s.items, uint32(key))
-}
-
-// Get looks up the element indexed under key.
-func (s *shard) Get(key uint32) (interface{}, bool) {
- s.RLock()
- el, found := s.items[key]
- s.RUnlock()
- return el, found
-}
-
-// Len returns the current length of the cache.
-func (s *shard) Len() int {
- s.RLock()
- l := len(s.items)
- s.RUnlock()
- return l
-}
-
-const shardSize = 256
diff --git a/middleware/pkg/cache/cache_test.go b/middleware/pkg/cache/cache_test.go
deleted file mode 100644
index 2c92bf438..000000000
--- a/middleware/pkg/cache/cache_test.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package cache
-
-import "testing"
-
-func TestCacheAddAndGet(t *testing.T) {
- c := New(4)
- c.Add(1, 1)
-
- if _, found := c.Get(1); !found {
- t.Fatal("Failed to find inserted record")
- }
-}
-
-func TestCacheLen(t *testing.T) {
- c := New(4)
-
- c.Add(1, 1)
- if l := c.Len(); l != 1 {
- t.Fatalf("Cache size should %d, got %d", 1, l)
- }
-
- c.Add(1, 1)
- if l := c.Len(); l != 1 {
- t.Fatalf("Cache size should %d, got %d", 1, l)
- }
-
- c.Add(2, 2)
- if l := c.Len(); l != 2 {
- t.Fatalf("Cache size should %d, got %d", 2, l)
- }
-}
diff --git a/middleware/pkg/cache/shard_test.go b/middleware/pkg/cache/shard_test.go
deleted file mode 100644
index 26675cee1..000000000
--- a/middleware/pkg/cache/shard_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package cache
-
-import "testing"
-
-func TestShardAddAndGet(t *testing.T) {
- s := newShard(4)
- s.Add(1, 1)
-
- if _, found := s.Get(1); !found {
- t.Fatal("Failed to find inserted record")
- }
-}
-
-func TestShardLen(t *testing.T) {
- s := newShard(4)
-
- s.Add(1, 1)
- if l := s.Len(); l != 1 {
- t.Fatalf("Shard size should %d, got %d", 1, l)
- }
-
- s.Add(1, 1)
- if l := s.Len(); l != 1 {
- t.Fatalf("Shard size should %d, got %d", 1, l)
- }
-
- s.Add(2, 2)
- if l := s.Len(); l != 2 {
- t.Fatalf("Shard size should %d, got %d", 2, l)
- }
-}
-
-func TestShardEvict(t *testing.T) {
- s := newShard(1)
- s.Add(1, 1)
- s.Add(2, 2)
- // 1 should be gone
-
- if _, found := s.Get(1); found {
- t.Fatal("Found item that should have been evicted")
- }
-}
-
-func TestShardLenEvict(t *testing.T) {
- s := newShard(4)
- s.Add(1, 1)
- s.Add(2, 1)
- s.Add(3, 1)
- s.Add(4, 1)
-
- if l := s.Len(); l != 4 {
- t.Fatalf("Shard size should %d, got %d", 4, l)
- }
-
- // This should evict one element
- s.Add(5, 1)
- if l := s.Len(); l != 4 {
- t.Fatalf("Shard size should %d, got %d", 4, l)
- }
-}
diff --git a/middleware/pkg/dnsrecorder/recorder.go b/middleware/pkg/dnsrecorder/recorder.go
deleted file mode 100644
index 3ca5f00d0..000000000
--- a/middleware/pkg/dnsrecorder/recorder.go
+++ /dev/null
@@ -1,58 +0,0 @@
-// Package dnsrecorder allows you to record a DNS response when it is send to the client.
-package dnsrecorder
-
-import (
- "time"
-
- "github.com/miekg/dns"
-)
-
-// Recorder is a type of ResponseWriter that captures
-// the rcode code written to it and also the size of the message
-// written in the response. A rcode code does not have
-// to be written, however, in which case 0 must be assumed.
-// It is best to have the constructor initialize this type
-// with that default status code.
-type Recorder struct {
- dns.ResponseWriter
- Rcode int
- Len int
- Msg *dns.Msg
- Start time.Time
-}
-
-// New makes and returns a new Recorder,
-// which captures the DNS rcode from the ResponseWriter
-// and also the length of the response message written through it.
-func New(w dns.ResponseWriter) *Recorder {
- return &Recorder{
- ResponseWriter: w,
- Rcode: 0,
- Msg: nil,
- Start: time.Now(),
- }
-}
-
-// WriteMsg records the status code and calls the
-// underlying ResponseWriter's WriteMsg method.
-func (r *Recorder) WriteMsg(res *dns.Msg) error {
- r.Rcode = res.Rcode
- // We may get called multiple times (axfr for instance).
- // Save the last message, but add the sizes.
- r.Len += res.Len()
- r.Msg = res
- return r.ResponseWriter.WriteMsg(res)
-}
-
-// Write is a wrapper that records the length of the message that gets written.
-func (r *Recorder) Write(buf []byte) (int, error) {
- n, err := r.ResponseWriter.Write(buf)
- if err == nil {
- r.Len += n
- }
- return n, err
-}
-
-// Hijack implements dns.Hijacker. It simply wraps the underlying
-// ResponseWriter's Hijack method if there is one, or returns an error.
-func (r *Recorder) Hijack() { r.ResponseWriter.Hijack(); return }
diff --git a/middleware/pkg/dnsrecorder/recorder_test.go b/middleware/pkg/dnsrecorder/recorder_test.go
deleted file mode 100644
index c9c2f6ce4..000000000
--- a/middleware/pkg/dnsrecorder/recorder_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package dnsrecorder
-
-/*
-func TestNewResponseRecorder(t *testing.T) {
- w := httptest.NewRecorder()
- recordRequest := NewResponseRecorder(w)
- if !(recordRequest.ResponseWriter == w) {
- t.Fatalf("Expected Response writer in the Recording to be same as the one sent\n")
- }
- if recordRequest.status != http.StatusOK {
- t.Fatalf("Expected recorded status to be http.StatusOK (%d) , but found %d\n ", http.StatusOK, recordRequest.status)
- }
-}
-
-func TestWrite(t *testing.T) {
- w := httptest.NewRecorder()
- responseTestString := "test"
- recordRequest := NewResponseRecorder(w)
- buf := []byte(responseTestString)
- recordRequest.Write(buf)
- if recordRequest.size != len(buf) {
- t.Fatalf("Expected the bytes written counter to be %d, but instead found %d\n", len(buf), recordRequest.size)
- }
- if w.Body.String() != responseTestString {
- t.Fatalf("Expected Response Body to be %s , but found %s\n", responseTestString, w.Body.String())
- }
-}
-*/
diff --git a/middleware/pkg/dnsutil/cname.go b/middleware/pkg/dnsutil/cname.go
deleted file mode 100644
index 281e03218..000000000
--- a/middleware/pkg/dnsutil/cname.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package dnsutil
-
-import "github.com/miekg/dns"
-
-// DuplicateCNAME returns true if r already exists in records.
-func DuplicateCNAME(r *dns.CNAME, records []dns.RR) bool {
- for _, rec := range records {
- if v, ok := rec.(*dns.CNAME); ok {
- if v.Target == r.Target {
- return true
- }
- }
- }
- return false
-}
diff --git a/middleware/pkg/dnsutil/cname_test.go b/middleware/pkg/dnsutil/cname_test.go
deleted file mode 100644
index 5fb8d3029..000000000
--- a/middleware/pkg/dnsutil/cname_test.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package dnsutil
-
-import (
- "testing"
-
- "github.com/miekg/dns"
-)
-
-func TestDuplicateCNAME(t *testing.T) {
- tests := []struct {
- cname string
- records []string
- expected bool
- }{
- {
- "1.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 1.0.0.0.192.IN-ADDR.ARPA.",
- []string{
- "US. 86400 IN NSEC 0-.us. NS SOA RRSIG NSEC DNSKEY TYPE65534",
- "1.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 1.0.0.0.192.IN-ADDR.ARPA.",
- },
- true,
- },
- {
- "1.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 1.0.0.0.192.IN-ADDR.ARPA.",
- []string{
- "US. 86400 IN NSEC 0-.us. NS SOA RRSIG NSEC DNSKEY TYPE65534",
- },
- false,
- },
- {
- "1.0.0.192.IN-ADDR.ARPA. 3600 IN CNAME 1.0.0.0.192.IN-ADDR.ARPA.",
- []string{},
- false,
- },
- }
- for i, test := range tests {
- cnameRR, err := dns.NewRR(test.cname)
- if err != nil {
- t.Fatalf("Test %d, cname ('%s') error (%s)!", i, test.cname, err)
- }
- cname := cnameRR.(*dns.CNAME)
- records := []dns.RR{}
- for j, r := range test.records {
- rr, err := dns.NewRR(r)
- if err != nil {
- t.Fatalf("Test %d, record %d ('%s') error (%s)!", i, j, r, err)
- }
- records = append(records, rr)
- }
- got := DuplicateCNAME(cname, records)
- if got != test.expected {
- t.Errorf("Test %d, expected '%v', got '%v' for CNAME ('%s') and RECORDS (%v)", i, test.expected, got, test.cname, test.records)
- }
- }
-}
diff --git a/middleware/pkg/dnsutil/dedup.go b/middleware/pkg/dnsutil/dedup.go
deleted file mode 100644
index dae656a01..000000000
--- a/middleware/pkg/dnsutil/dedup.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package dnsutil
-
-import "github.com/miekg/dns"
-
-// Dedup de-duplicates a message.
-func Dedup(m *dns.Msg) *dns.Msg {
- // TODO(miek): expensive!
- m.Answer = dns.Dedup(m.Answer, nil)
- m.Ns = dns.Dedup(m.Ns, nil)
- m.Extra = dns.Dedup(m.Extra, nil)
- return m
-}
diff --git a/middleware/pkg/dnsutil/doc.go b/middleware/pkg/dnsutil/doc.go
deleted file mode 100644
index 75d1e8c7a..000000000
--- a/middleware/pkg/dnsutil/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package dnsutil contains DNS related helper functions.
-package dnsutil
diff --git a/middleware/pkg/dnsutil/host.go b/middleware/pkg/dnsutil/host.go
deleted file mode 100644
index aaab586e8..000000000
--- a/middleware/pkg/dnsutil/host.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package dnsutil
-
-import (
- "fmt"
- "net"
- "os"
-
- "github.com/miekg/dns"
-)
-
-// ParseHostPortOrFile parses the strings in s, each string can either be a address,
-// address:port or a filename. The address part is checked and the filename case a
-// resolv.conf like file is parsed and the nameserver found are returned.
-func ParseHostPortOrFile(s ...string) ([]string, error) {
- var servers []string
- for _, host := range s {
- 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)
- }
- ss := net.JoinHostPort(host, "53")
- servers = append(servers, ss)
- continue
- }
-
- if net.ParseIP(addr) == nil {
- // No 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, host)
- }
- 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
-}
-
-// ParseHostPort 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 ParseHostPort(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/middleware/pkg/dnsutil/host_test.go b/middleware/pkg/dnsutil/host_test.go
deleted file mode 100644
index cc55f4570..000000000
--- a/middleware/pkg/dnsutil/host_test.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package dnsutil
-
-import (
- "io/ioutil"
- "os"
- "testing"
-)
-
-func TestParseHostPortOrFile(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 := ParseHostPortOrFile(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 := ParseHostPort(tc.in, "53")
- 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/middleware/pkg/dnsutil/join.go b/middleware/pkg/dnsutil/join.go
deleted file mode 100644
index 515bf3dad..000000000
--- a/middleware/pkg/dnsutil/join.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package dnsutil
-
-import (
- "strings"
-
- "github.com/miekg/dns"
-)
-
-// Join joins labels to form a fully qualified domain name. If the last label is
-// the root label it is ignored. Not other syntax checks are performed.
-func Join(labels []string) string {
- ll := len(labels)
- if labels[ll-1] == "." {
- s := strings.Join(labels[:ll-1], ".")
- return dns.Fqdn(s)
- }
- s := strings.Join(labels, ".")
- return dns.Fqdn(s)
-}
diff --git a/middleware/pkg/dnsutil/join_test.go b/middleware/pkg/dnsutil/join_test.go
deleted file mode 100644
index 26eeb5897..000000000
--- a/middleware/pkg/dnsutil/join_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package dnsutil
-
-import "testing"
-
-func TestJoin(t *testing.T) {
- tests := []struct {
- in []string
- out string
- }{
- {[]string{"bla", "bliep", "example", "org"}, "bla.bliep.example.org."},
- {[]string{"example", "."}, "example."},
- {[]string{"."}, "."},
- }
-
- for i, tc := range tests {
- if x := Join(tc.in); x != tc.out {
- t.Errorf("Test %d, expected %s, got %s", i, tc.out, x)
- }
- }
-}
diff --git a/middleware/pkg/dnsutil/reverse.go b/middleware/pkg/dnsutil/reverse.go
deleted file mode 100644
index daf9cc600..000000000
--- a/middleware/pkg/dnsutil/reverse.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package dnsutil
-
-import (
- "net"
- "strings"
-)
-
-// ExtractAddressFromReverse turns a standard PTR reverse record name
-// into an IP address. This works for ipv4 or ipv6.
-//
-// 54.119.58.176.in-addr.arpa. becomes 176.58.119.54. If the conversion
-// failes the empty string is returned.
-func ExtractAddressFromReverse(reverseName string) string {
- search := ""
-
- f := reverse
-
- switch {
- case strings.HasSuffix(reverseName, v4arpaSuffix):
- search = strings.TrimSuffix(reverseName, v4arpaSuffix)
- case strings.HasSuffix(reverseName, v6arpaSuffix):
- search = strings.TrimSuffix(reverseName, v6arpaSuffix)
- f = reverse6
- default:
- return ""
- }
-
- // Reverse the segments and then combine them.
- return f(strings.Split(search, "."))
-}
-
-func reverse(slice []string) string {
- for i := 0; i < len(slice)/2; i++ {
- j := len(slice) - i - 1
- slice[i], slice[j] = slice[j], slice[i]
- }
- ip := net.ParseIP(strings.Join(slice, ".")).To4()
- if ip == nil {
- return ""
- }
- return ip.String()
-}
-
-// reverse6 reverse the segments and combine them according to RFC3596:
-// b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2
-// is reversed to 2001:db8::567:89ab
-func reverse6(slice []string) string {
- for i := 0; i < len(slice)/2; i++ {
- j := len(slice) - i - 1
- slice[i], slice[j] = slice[j], slice[i]
- }
- slice6 := []string{}
- for i := 0; i < len(slice)/4; i++ {
- slice6 = append(slice6, strings.Join(slice[i*4:i*4+4], ""))
- }
- ip := net.ParseIP(strings.Join(slice6, ":")).To16()
- if ip == nil {
- return ""
- }
- return ip.String()
-}
-
-const (
- // v4arpaSuffix is the reverse tree suffix for v4 IP addresses.
- v4arpaSuffix = ".in-addr.arpa."
- // v6arpaSuffix is the reverse tree suffix for v6 IP addresses.
- v6arpaSuffix = ".ip6.arpa."
-)
diff --git a/middleware/pkg/dnsutil/reverse_test.go b/middleware/pkg/dnsutil/reverse_test.go
deleted file mode 100644
index 25bd897ac..000000000
--- a/middleware/pkg/dnsutil/reverse_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package dnsutil
-
-import (
- "testing"
-)
-
-func TestExtractAddressFromReverse(t *testing.T) {
- tests := []struct {
- reverseName string
- expectedAddress string
- }{
- {
- "54.119.58.176.in-addr.arpa.",
- "176.58.119.54",
- },
- {
- ".58.176.in-addr.arpa.",
- "",
- },
- {
- "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.in-addr.arpa.",
- "",
- },
- {
- "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
- "2001:db8::567:89ab",
- },
- {
- "d.0.1.0.0.2.ip6.arpa.",
- "",
- },
- {
- "54.119.58.176.ip6.arpa.",
- "",
- },
- {
- "NONAME",
- "",
- },
- {
- "",
- "",
- },
- }
- for i, test := range tests {
- got := ExtractAddressFromReverse(test.reverseName)
- if got != test.expectedAddress {
- t.Errorf("Test %d, expected '%s', got '%s'", i, test.expectedAddress, got)
- }
- }
-}
diff --git a/middleware/pkg/dnsutil/zone.go b/middleware/pkg/dnsutil/zone.go
deleted file mode 100644
index 579fef1ba..000000000
--- a/middleware/pkg/dnsutil/zone.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package dnsutil
-
-import (
- "errors"
-
- "github.com/miekg/dns"
-)
-
-// TrimZone removes the zone component from q. It returns the trimmed
-// name or an error is zone is longer then qname. The trimmed name will be returned
-// without a trailing dot.
-func TrimZone(q string, z string) (string, error) {
- zl := dns.CountLabel(z)
- i, ok := dns.PrevLabel(q, zl)
- if ok || i-1 < 0 {
- return "", errors.New("trimzone: overshot qname: " + q + "for zone " + z)
- }
- // This includes the '.', remove on return
- return q[:i-1], nil
-}
diff --git a/middleware/pkg/dnsutil/zone_test.go b/middleware/pkg/dnsutil/zone_test.go
deleted file mode 100644
index 81cd1adad..000000000
--- a/middleware/pkg/dnsutil/zone_test.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package dnsutil
-
-import (
- "errors"
- "testing"
-
- "github.com/miekg/dns"
-)
-
-func TestTrimZone(t *testing.T) {
- tests := []struct {
- qname string
- zone string
- expected string
- err error
- }{
- {"a.example.org", "example.org", "a", nil},
- {"a.b.example.org", "example.org", "a.b", nil},
- {"b.", ".", "b", nil},
- {"example.org", "example.org", "", errors.New("should err")},
- {"org", "example.org", "", errors.New("should err")},
- }
-
- for i, tc := range tests {
- got, err := TrimZone(dns.Fqdn(tc.qname), dns.Fqdn(tc.zone))
- if tc.err != nil && err == nil {
- t.Errorf("Test %d, expected error got nil", i)
- continue
- }
- if tc.err == nil && err != nil {
- t.Errorf("Test %d, expected no error got %v", i, err)
- continue
- }
- if got != tc.expected {
- t.Errorf("Test %d, expected %s, got %s", i, tc.expected, got)
- continue
- }
- }
-}
diff --git a/middleware/pkg/edns/edns.go b/middleware/pkg/edns/edns.go
deleted file mode 100644
index 6cbe2cdae..000000000
--- a/middleware/pkg/edns/edns.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// Package edns provides function useful for adding/inspecting OPT records to/in messages.
-package edns
-
-import (
- "errors"
-
- "github.com/miekg/dns"
-)
-
-// Version checks the EDNS version in the request. If error
-// is nil everything is OK and we can invoke the middleware. If non-nil, the
-// returned Msg is valid to be returned to the client (and should). For some
-// reason this response should not contain a question RR in the question section.
-func Version(req *dns.Msg) (*dns.Msg, error) {
- opt := req.IsEdns0()
- if opt == nil {
- return nil, nil
- }
- if opt.Version() == 0 {
- return nil, nil
- }
- m := new(dns.Msg)
- m.SetReply(req)
- // zero out question section, wtf.
- m.Question = nil
-
- o := new(dns.OPT)
- o.Hdr.Name = "."
- o.Hdr.Rrtype = dns.TypeOPT
- o.SetVersion(0)
- o.SetExtendedRcode(dns.RcodeBadVers)
- m.Extra = []dns.RR{o}
-
- return m, errors.New("EDNS0 BADVERS")
-}
-
-// Size returns a normalized size based on proto.
-func Size(proto string, size int) int {
- if proto == "tcp" {
- return dns.MaxMsgSize
- }
- if size < dns.MinMsgSize {
- return dns.MinMsgSize
- }
- return size
-}
diff --git a/middleware/pkg/edns/edns_test.go b/middleware/pkg/edns/edns_test.go
deleted file mode 100644
index 89ac6d2ec..000000000
--- a/middleware/pkg/edns/edns_test.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package edns
-
-import (
- "testing"
-
- "github.com/miekg/dns"
-)
-
-func TestVersion(t *testing.T) {
- m := ednsMsg()
- m.Extra[0].(*dns.OPT).SetVersion(2)
-
- _, err := Version(m)
- if err == nil {
- t.Errorf("expected wrong version, but got OK")
- }
-}
-
-func TestVersionNoEdns(t *testing.T) {
- m := ednsMsg()
- m.Extra = nil
-
- _, err := Version(m)
- if err != nil {
- t.Errorf("expected no error, but got one: %s", err)
- }
-}
-
-func ednsMsg() *dns.Msg {
- m := new(dns.Msg)
- m.SetQuestion("example.com.", dns.TypeA)
- o := new(dns.OPT)
- o.Hdr.Name = "."
- o.Hdr.Rrtype = dns.TypeOPT
- m.Extra = append(m.Extra, o)
- return m
-}
diff --git a/middleware/pkg/healthcheck/healthcheck.go b/middleware/pkg/healthcheck/healthcheck.go
deleted file mode 100644
index 18f09087c..000000000
--- a/middleware/pkg/healthcheck/healthcheck.go
+++ /dev/null
@@ -1,243 +0,0 @@
-package healthcheck
-
-import (
- "io"
- "io/ioutil"
- "log"
- "net"
- "net/http"
- "net/url"
- "sync"
- "sync/atomic"
- "time"
-)
-
-// UpstreamHostDownFunc can be used to customize how Down behaves.
-type UpstreamHostDownFunc func(*UpstreamHost) bool
-
-// UpstreamHost represents a single proxy upstream
-type UpstreamHost struct {
- Conns int64 // must be first field to be 64-bit aligned on 32-bit systems
- Name string // IP address (and port) of this upstream host
- Network string // Network (tcp, unix, etc) of the host, default "" is "tcp"
- Fails int32
- FailTimeout time.Duration
- OkUntil time.Time
- CheckDown UpstreamHostDownFunc
- CheckURL string
- WithoutPathPrefix string
- Checking bool
- CheckMu sync.Mutex
-}
-
-// Down checks whether the upstream host is down or not.
-// Down will try to use uh.CheckDown first, and will fall
-// back to some default criteria if necessary.
-func (uh *UpstreamHost) Down() bool {
- if uh.CheckDown == nil {
- // Default settings
- fails := atomic.LoadInt32(&uh.Fails)
- after := false
-
- uh.CheckMu.Lock()
- until := uh.OkUntil
- uh.CheckMu.Unlock()
-
- if !until.IsZero() && time.Now().After(until) {
- after = true
- }
-
- return after || fails > 0
- }
- return uh.CheckDown(uh)
-}
-
-// HostPool is a collection of UpstreamHosts.
-type HostPool []*UpstreamHost
-
-// HealthCheck is used for performing healthcheck
-// on a collection of upstream hosts and select
-// one based on the policy.
-type HealthCheck struct {
- wg sync.WaitGroup // Used to wait for running goroutines to stop.
- stop chan struct{} // Signals running goroutines to stop.
- Hosts HostPool
- Policy Policy
- Spray Policy
- FailTimeout time.Duration
- MaxFails int32
- Future time.Duration
- Path string
- Port string
- Interval time.Duration
-}
-
-// Start starts the healthcheck
-func (u *HealthCheck) Start() {
- u.stop = make(chan struct{})
- if u.Path != "" {
- u.wg.Add(1)
- go func() {
- defer u.wg.Done()
- u.healthCheckWorker(u.stop)
- }()
- }
-}
-
-// Stop sends a signal to all goroutines started by this staticUpstream to exit
-// and waits for them to finish before returning.
-func (u *HealthCheck) Stop() error {
- close(u.stop)
- u.wg.Wait()
- return nil
-}
-
-// This was moved into a thread so that each host could throw a health
-// check at the same time. The reason for this is that if we are checking
-// 3 hosts, and the first one is gone, and we spend minutes timing out to
-// fail it, we would not have been doing any other health checks in that
-// time. So we now have a per-host lock and a threaded health check.
-//
-// We use the Checking bool to avoid concurrent checks against the same
-// host; if one is taking a long time, the next one will find a check in
-// progress and simply return before trying.
-//
-// We are carefully avoiding having the mutex locked while we check,
-// otherwise checks will back up, potentially a lot of them if a host is
-// absent for a long time. This arrangement makes checks quickly see if
-// they are the only one running and abort otherwise.
-func healthCheckURL(nextTs time.Time, host *UpstreamHost) {
-
- // lock for our bool check. We don't just defer the unlock because
- // we don't want the lock held while http.Get runs
- host.CheckMu.Lock()
-
- // are we mid check? Don't run another one
- if host.Checking {
- host.CheckMu.Unlock()
- return
- }
-
- host.Checking = true
- host.CheckMu.Unlock()
-
- //log.Printf("[DEBUG] Healthchecking %s, nextTs is %s\n", url, nextTs.Local())
-
- // fetch that url. This has been moved into a go func because
- // when the remote host is not merely not serving, but actually
- // absent, then tcp syn timeouts can be very long, and so one
- // fetch could last several check intervals
- if r, err := http.Get(host.CheckURL); err == nil {
- io.Copy(ioutil.Discard, r.Body)
- r.Body.Close()
-
- if r.StatusCode < 200 || r.StatusCode >= 400 {
- log.Printf("[WARNING] Host %s health check returned HTTP code %d\n",
- host.Name, r.StatusCode)
- nextTs = time.Unix(0, 0)
- }
- } else {
- log.Printf("[WARNING] Host %s health check probe failed: %v\n", host.Name, err)
- nextTs = time.Unix(0, 0)
- }
-
- host.CheckMu.Lock()
- host.Checking = false
- host.OkUntil = nextTs
- host.CheckMu.Unlock()
-}
-
-func (u *HealthCheck) healthCheck() {
- for _, host := range u.Hosts {
-
- if host.CheckURL == "" {
- var hostName, checkPort string
-
- // The DNS server might be an HTTP server. If so, extract its name.
- ret, err := url.Parse(host.Name)
- if err == nil && len(ret.Host) > 0 {
- hostName = ret.Host
- } else {
- hostName = host.Name
- }
-
- // Extract the port number from the parsed server name.
- checkHostName, checkPort, err := net.SplitHostPort(hostName)
- if err != nil {
- checkHostName = hostName
- }
-
- if u.Port != "" {
- checkPort = u.Port
- }
-
- host.CheckURL = "http://" + net.JoinHostPort(checkHostName, checkPort) + u.Path
- }
-
- // calculate this before the get
- nextTs := time.Now().Add(u.Future)
-
- // locks/bools should prevent requests backing up
- go healthCheckURL(nextTs, host)
- }
-}
-
-func (u *HealthCheck) healthCheckWorker(stop chan struct{}) {
- ticker := time.NewTicker(u.Interval)
- u.healthCheck()
- for {
- select {
- case <-ticker.C:
- u.healthCheck()
- case <-stop:
- ticker.Stop()
- return
- }
- }
-}
-
-// Select selects an upstream host based on the policy
-// and the healthcheck result.
-func (u *HealthCheck) Select() *UpstreamHost {
- pool := u.Hosts
- if len(pool) == 1 {
- if pool[0].Down() && u.Spray == nil {
- return nil
- }
- return pool[0]
- }
- allDown := true
- for _, host := range pool {
- if !host.Down() {
- allDown = false
- break
- }
- }
- if allDown {
- if u.Spray == nil {
- return nil
- }
- return u.Spray.Select(pool)
- }
-
- if u.Policy == nil {
- h := (&Random{}).Select(pool)
- if h != nil {
- return h
- }
- if h == nil && u.Spray == nil {
- return nil
- }
- return u.Spray.Select(pool)
- }
-
- h := u.Policy.Select(pool)
- if h != nil {
- return h
- }
-
- if u.Spray == nil {
- return nil
- }
- return u.Spray.Select(pool)
-}
diff --git a/middleware/pkg/healthcheck/policy.go b/middleware/pkg/healthcheck/policy.go
deleted file mode 100644
index 6a828fc4d..000000000
--- a/middleware/pkg/healthcheck/policy.go
+++ /dev/null
@@ -1,120 +0,0 @@
-package healthcheck
-
-import (
- "log"
- "math/rand"
- "sync/atomic"
-)
-
-var (
- // SupportedPolicies is the collection of policies registered
- SupportedPolicies = make(map[string]func() Policy)
-)
-
-// RegisterPolicy adds a custom policy to the proxy.
-func RegisterPolicy(name string, policy func() Policy) {
- SupportedPolicies[name] = policy
-}
-
-// Policy decides how a host will be selected from a pool. When all hosts are unhealthy, it is assumed the
-// healthchecking failed. In this case each policy will *randomly* return a host from the pool to prevent
-// no traffic to go through at all.
-type Policy interface {
- Select(pool HostPool) *UpstreamHost
-}
-
-func init() {
- RegisterPolicy("random", func() Policy { return &Random{} })
- RegisterPolicy("least_conn", func() Policy { return &LeastConn{} })
- RegisterPolicy("round_robin", func() Policy { return &RoundRobin{} })
-}
-
-// Random is a policy that selects up hosts from a pool at random.
-type Random struct{}
-
-// Select selects an up host at random from the specified pool.
-func (r *Random) Select(pool HostPool) *UpstreamHost {
- // instead of just generating a random index
- // this is done to prevent selecting a down host
- var randHost *UpstreamHost
- count := 0
- for _, host := range pool {
- if host.Down() {
- continue
- }
- count++
- if count == 1 {
- randHost = host
- } else {
- r := rand.Int() % count
- if r == (count - 1) {
- randHost = host
- }
- }
- }
- return randHost
-}
-
-// Spray is a policy that selects a host from a pool at random. This should be used as a last ditch
-// attempt to get a host when all hosts are reporting unhealthy.
-type Spray struct{}
-
-// Select selects an up host at random from the specified pool.
-func (r *Spray) Select(pool HostPool) *UpstreamHost {
- rnd := rand.Int() % len(pool)
- randHost := pool[rnd]
- log.Printf("[WARNING] All hosts reported as down, spraying to target: %s", randHost.Name)
- return randHost
-}
-
-// LeastConn is a policy that selects the host with the least connections.
-type LeastConn struct{}
-
-// Select selects the up host with the least number of connections in the
-// pool. If more than one host has the same least number of connections,
-// one of the hosts is chosen at random.
-func (r *LeastConn) Select(pool HostPool) *UpstreamHost {
- var bestHost *UpstreamHost
- count := 0
- leastConn := int64(1<<63 - 1)
- for _, host := range pool {
- if host.Down() {
- continue
- }
- hostConns := host.Conns
- if hostConns < leastConn {
- bestHost = host
- leastConn = hostConns
- count = 1
- } else if hostConns == leastConn {
- // randomly select host among hosts with least connections
- count++
- if count == 1 {
- bestHost = host
- } else {
- r := rand.Int() % count
- if r == (count - 1) {
- bestHost = host
- }
- }
- }
- }
- return bestHost
-}
-
-// RoundRobin is a policy that selects hosts based on round robin ordering.
-type RoundRobin struct {
- Robin uint32
-}
-
-// Select selects an up host from the pool using a round robin ordering scheme.
-func (r *RoundRobin) Select(pool HostPool) *UpstreamHost {
- poolLen := uint32(len(pool))
- selection := atomic.AddUint32(&r.Robin, 1) % poolLen
- host := pool[selection]
- // if the currently selected host is down, just ffwd to up host
- for i := uint32(1); host.Down() && i < poolLen; i++ {
- host = pool[(selection+i)%poolLen]
- }
- return host
-}
diff --git a/middleware/pkg/healthcheck/policy_test.go b/middleware/pkg/healthcheck/policy_test.go
deleted file mode 100644
index 4c667952c..000000000
--- a/middleware/pkg/healthcheck/policy_test.go
+++ /dev/null
@@ -1,143 +0,0 @@
-package healthcheck
-
-import (
- "io/ioutil"
- "log"
- "net/http"
- "net/http/httptest"
- "os"
- "testing"
- "time"
-)
-
-var workableServer *httptest.Server
-
-func TestMain(m *testing.M) {
- workableServer = httptest.NewServer(http.HandlerFunc(
- func(w http.ResponseWriter, r *http.Request) {
- // do nothing
- }))
- r := m.Run()
- workableServer.Close()
- os.Exit(r)
-}
-
-type customPolicy struct{}
-
-func (r *customPolicy) Select(pool HostPool) *UpstreamHost {
- return pool[0]
-}
-
-func testPool() HostPool {
- pool := []*UpstreamHost{
- {
- Name: workableServer.URL, // this should resolve (healthcheck test)
- },
- {
- Name: "http://shouldnot.resolve", // this shouldn't
- },
- {
- Name: "http://C",
- },
- }
- return HostPool(pool)
-}
-
-func TestRegisterPolicy(t *testing.T) {
- name := "custom"
- customPolicy := &customPolicy{}
- RegisterPolicy(name, func() Policy { return customPolicy })
- if _, ok := SupportedPolicies[name]; !ok {
- t.Error("Expected supportedPolicies to have a custom policy.")
- }
-
-}
-
-// TODO(miek): Disabled for now, we should get out of the habit of using
-// realtime in these tests .
-func testHealthCheck(t *testing.T) {
- log.SetOutput(ioutil.Discard)
-
- u := &HealthCheck{
- Hosts: testPool(),
- FailTimeout: 10 * time.Second,
- Future: 60 * time.Second,
- MaxFails: 1,
- }
-
- u.healthCheck()
- // sleep a bit, it's async now
- time.Sleep(time.Duration(2 * time.Second))
-
- if u.Hosts[0].Down() {
- t.Error("Expected first host in testpool to not fail healthcheck.")
- }
- if !u.Hosts[1].Down() {
- t.Error("Expected second host in testpool to fail healthcheck.")
- }
-}
-
-func TestSelect(t *testing.T) {
- u := &HealthCheck{
- Hosts: testPool()[:3],
- FailTimeout: 10 * time.Second,
- Future: 60 * time.Second,
- MaxFails: 1,
- }
- u.Hosts[0].OkUntil = time.Unix(0, 0)
- u.Hosts[1].OkUntil = time.Unix(0, 0)
- u.Hosts[2].OkUntil = time.Unix(0, 0)
- if h := u.Select(); h != nil {
- t.Error("Expected select to return nil as all host are down")
- }
- u.Hosts[2].OkUntil = time.Time{}
- if h := u.Select(); h == nil {
- t.Error("Expected select to not return nil")
- }
-}
-
-func TestRoundRobinPolicy(t *testing.T) {
- pool := testPool()
- rrPolicy := &RoundRobin{}
- h := rrPolicy.Select(pool)
- // First selected host is 1, because counter starts at 0
- // and increments before host is selected
- if h != pool[1] {
- t.Error("Expected first round robin host to be second host in the pool.")
- }
- h = rrPolicy.Select(pool)
- if h != pool[2] {
- t.Error("Expected second round robin host to be third host in the pool.")
- }
- // mark host as down
- pool[0].OkUntil = time.Unix(0, 0)
- h = rrPolicy.Select(pool)
- if h != pool[1] {
- t.Error("Expected third round robin host to be first host in the pool.")
- }
-}
-
-func TestLeastConnPolicy(t *testing.T) {
- pool := testPool()
- lcPolicy := &LeastConn{}
- pool[0].Conns = 10
- pool[1].Conns = 10
- h := lcPolicy.Select(pool)
- if h != pool[2] {
- t.Error("Expected least connection host to be third host.")
- }
- pool[2].Conns = 100
- h = lcPolicy.Select(pool)
- if h != pool[0] && h != pool[1] {
- t.Error("Expected least connection host to be first or second host.")
- }
-}
-
-func TestCustomPolicy(t *testing.T) {
- pool := testPool()
- customPolicy := &customPolicy{}
- h := customPolicy.Select(pool)
- if h != pool[0] {
- t.Error("Expected custom policy host to be the first host.")
- }
-}
diff --git a/middleware/pkg/nonwriter/nonwriter.go b/middleware/pkg/nonwriter/nonwriter.go
deleted file mode 100644
index 7819a320f..000000000
--- a/middleware/pkg/nonwriter/nonwriter.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Package nonwriter implements a dns.ResponseWriter that never writes, but captures the dns.Msg being written.
-package nonwriter
-
-import (
- "github.com/miekg/dns"
-)
-
-// Writer is a type of ResponseWriter that captures the message, but never writes to the client.
-type Writer struct {
- dns.ResponseWriter
- Msg *dns.Msg
-}
-
-// New makes and returns a new NonWriter.
-func New(w dns.ResponseWriter) *Writer { return &Writer{ResponseWriter: w} }
-
-// WriteMsg records the message, but doesn't write it itself.
-func (w *Writer) WriteMsg(res *dns.Msg) error {
- w.Msg = res
- return nil
-}
-
-func (w *Writer) Write(buf []byte) (int, error) { return len(buf), nil }
diff --git a/middleware/pkg/nonwriter/nonwriter_test.go b/middleware/pkg/nonwriter/nonwriter_test.go
deleted file mode 100644
index d8433af55..000000000
--- a/middleware/pkg/nonwriter/nonwriter_test.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package nonwriter
-
-import (
- "testing"
-
- "github.com/miekg/dns"
-)
-
-func TestNonWriter(t *testing.T) {
- nw := New(nil)
- m := new(dns.Msg)
- m.SetQuestion("example.org.", dns.TypeA)
- if err := nw.WriteMsg(m); err != nil {
- t.Errorf("Got error when writing to nonwriter: %s", err)
- }
- if x := nw.Msg.Question[0].Name; x != "example.org." {
- t.Errorf("Expacted 'example.org.' got %q:", x)
- }
-}
diff --git a/middleware/pkg/rcode/rcode.go b/middleware/pkg/rcode/rcode.go
deleted file mode 100644
index 32863f0b2..000000000
--- a/middleware/pkg/rcode/rcode.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package rcode
-
-import (
- "strconv"
-
- "github.com/miekg/dns"
-)
-
-// ToString convert the rcode to the official DNS string, or to "RCODE"+value if the RCODE
-// value is unknown.
-func ToString(rcode int) string {
- if str, ok := dns.RcodeToString[rcode]; ok {
- return str
- }
- return "RCODE" + strconv.Itoa(rcode)
-}
diff --git a/middleware/pkg/rcode/rcode_test.go b/middleware/pkg/rcode/rcode_test.go
deleted file mode 100644
index bfca32f1d..000000000
--- a/middleware/pkg/rcode/rcode_test.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package rcode
-
-import (
- "testing"
-
- "github.com/miekg/dns"
-)
-
-func TestToString(t *testing.T) {
- tests := []struct {
- in int
- expected string
- }{
- {
- dns.RcodeSuccess,
- "NOERROR",
- },
- {
- 28,
- "RCODE28",
- },
- }
- for i, test := range tests {
- got := ToString(test.in)
- if got != test.expected {
- t.Errorf("Test %d, expected %s, got %s", i, test.expected, got)
- }
- }
-}
diff --git a/middleware/pkg/replacer/replacer.go b/middleware/pkg/replacer/replacer.go
deleted file mode 100644
index 91f17e17c..000000000
--- a/middleware/pkg/replacer/replacer.go
+++ /dev/null
@@ -1,161 +0,0 @@
-package replacer
-
-import (
- "strconv"
- "strings"
- "time"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-// Replacer is a type which can replace placeholder
-// substrings in a string with actual values from a
-// dns.Msg and responseRecorder. Always use
-// NewReplacer to get one of these.
-type Replacer interface {
- Replace(string) string
- Set(key, value string)
-}
-
-type replacer struct {
- replacements map[string]string
- emptyValue string
-}
-
-// New makes a new replacer based on r and rr.
-// Do not create a new replacer until r and rr have all
-// the needed values, because this function copies those
-// values into the replacer. rr may be nil if it is not
-// available. emptyValue should be the string that is used
-// in place of empty string (can still be empty string).
-func New(r *dns.Msg, rr *dnsrecorder.Recorder, emptyValue string) Replacer {
- req := request.Request{W: rr, Req: r}
- rep := replacer{
- replacements: map[string]string{
- "{type}": req.Type(),
- "{name}": req.Name(),
- "{class}": req.Class(),
- "{proto}": req.Proto(),
- "{when}": func() string {
- return time.Now().Format(timeFormat)
- }(),
- "{size}": strconv.Itoa(req.Len()),
- "{remote}": req.IP(),
- "{port}": req.Port(),
- },
- emptyValue: emptyValue,
- }
- if rr != nil {
- rcode := dns.RcodeToString[rr.Rcode]
- if rcode == "" {
- rcode = strconv.Itoa(rr.Rcode)
- }
- rep.replacements["{rcode}"] = rcode
- rep.replacements["{rsize}"] = strconv.Itoa(rr.Len)
- rep.replacements["{duration}"] = time.Since(rr.Start).String()
- if rr.Msg != nil {
- rep.replacements[headerReplacer+"rflags}"] = flagsToString(rr.Msg.MsgHdr)
- }
- }
-
- // Header placeholders (case-insensitive)
- rep.replacements[headerReplacer+"id}"] = strconv.Itoa(int(r.Id))
- rep.replacements[headerReplacer+"opcode}"] = strconv.Itoa(r.Opcode)
- rep.replacements[headerReplacer+"do}"] = boolToString(req.Do())
- rep.replacements[headerReplacer+"bufsize}"] = strconv.Itoa(req.Size())
-
- return rep
-}
-
-// Replace performs a replacement of values on s and returns
-// the string with the replaced values.
-func (r replacer) Replace(s string) string {
- // Header replacements - these are case-insensitive, so we can't just use strings.Replace()
- for strings.Contains(s, headerReplacer) {
- idxStart := strings.Index(s, headerReplacer)
- endOffset := idxStart + len(headerReplacer)
- idxEnd := strings.Index(s[endOffset:], "}")
- if idxEnd > -1 {
- placeholder := strings.ToLower(s[idxStart : endOffset+idxEnd+1])
- replacement := r.replacements[placeholder]
- if replacement == "" {
- replacement = r.emptyValue
- }
- s = s[:idxStart] + replacement + s[endOffset+idxEnd+1:]
- } else {
- break
- }
- }
-
- // Regular replacements - these are easier because they're case-sensitive
- for placeholder, replacement := range r.replacements {
- if replacement == "" {
- replacement = r.emptyValue
- }
- s = strings.Replace(s, placeholder, replacement, -1)
- }
-
- return s
-}
-
-// Set sets key to value in the replacements map.
-func (r replacer) Set(key, value string) {
- r.replacements["{"+key+"}"] = value
-}
-
-func boolToString(b bool) string {
- if b {
- return "true"
- }
- return "false"
-}
-
-// flagsToString checks all header flags and returns those
-// that are set as a string separated with commas
-func flagsToString(h dns.MsgHdr) string {
- flags := make([]string, 7)
- i := 0
-
- if h.Response {
- flags[i] = "qr"
- i++
- }
-
- if h.Authoritative {
- flags[i] = "aa"
- i++
- }
- if h.Truncated {
- flags[i] = "tc"
- i++
- }
- if h.RecursionDesired {
- flags[i] = "rd"
- i++
- }
- if h.RecursionAvailable {
- flags[i] = "ra"
- i++
- }
- if h.Zero {
- flags[i] = "z"
- i++
- }
- if h.AuthenticatedData {
- flags[i] = "ad"
- i++
- }
- if h.CheckingDisabled {
- flags[i] = "cd"
- i++
- }
- return strings.Join(flags[:i], ",")
-}
-
-const (
- timeFormat = "02/Jan/2006:15:04:05 -0700"
- headerReplacer = "{>"
-)
diff --git a/middleware/pkg/replacer/replacer_test.go b/middleware/pkg/replacer/replacer_test.go
deleted file mode 100644
index a72f429a8..000000000
--- a/middleware/pkg/replacer/replacer_test.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package replacer
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
-)
-
-func TestNewReplacer(t *testing.T) {
- w := dnsrecorder.New(&test.ResponseWriter{})
-
- r := new(dns.Msg)
- r.SetQuestion("example.org.", dns.TypeHINFO)
- r.MsgHdr.AuthenticatedData = true
-
- replaceValues := New(r, w, "")
-
- switch v := replaceValues.(type) {
- case replacer:
-
- if v.replacements["{type}"] != "HINFO" {
- t.Errorf("Expected type to be HINFO, got %q", v.replacements["{type}"])
- }
- if v.replacements["{name}"] != "example.org." {
- t.Errorf("Expected request name to be example.org., got %q", v.replacements["{name}"])
- }
- if v.replacements["{size}"] != "29" { // size of request
- t.Errorf("Expected size to be 29, got %q", v.replacements["{size}"])
- }
-
- default:
- t.Fatal("Return Value from New Replacer expected pass type assertion into a replacer type\n")
- }
-}
-
-func TestSet(t *testing.T) {
- w := dnsrecorder.New(&test.ResponseWriter{})
-
- r := new(dns.Msg)
- r.SetQuestion("example.org.", dns.TypeHINFO)
- r.MsgHdr.AuthenticatedData = true
-
- repl := New(r, w, "")
-
- repl.Set("name", "coredns.io.")
- repl.Set("type", "A")
- repl.Set("size", "20")
-
- if repl.Replace("This name is {name}") != "This name is coredns.io." {
- t.Error("Expected name replacement failed")
- }
- if repl.Replace("This type is {type}") != "This type is A" {
- t.Error("Expected type replacement failed")
- }
- if repl.Replace("The request size is {size}") != "The request size is 20" {
- t.Error("Expected size replacement failed")
- }
-}
diff --git a/middleware/pkg/response/classify.go b/middleware/pkg/response/classify.go
deleted file mode 100644
index 2e705cb0b..000000000
--- a/middleware/pkg/response/classify.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package response
-
-import "fmt"
-
-// Class holds sets of Types
-type Class int
-
-const (
- // All is a meta class encompassing all the classes.
- All Class = iota
- // Success is a class for a successful response.
- Success
- // Denial is a class for denying existence (NXDOMAIN, or a nodata: type does not exist)
- Denial
- // Error is a class for errors, right now defined as not Success and not Denial
- Error
-)
-
-func (c Class) String() string {
- switch c {
- case All:
- return "all"
- case Success:
- return "success"
- case Denial:
- return "denial"
- case Error:
- return "error"
- }
- return ""
-}
-
-// ClassFromString returns the class from the string s. If not class matches
-// the All class and an error are returned
-func ClassFromString(s string) (Class, error) {
- switch s {
- case "all":
- return All, nil
- case "success":
- return Success, nil
- case "denial":
- return Denial, nil
- case "error":
- return Error, nil
- }
- return All, fmt.Errorf("invalid Class: %s", s)
-}
-
-// Classify classifies the Type t, it returns its Class.
-func Classify(t Type) Class {
- switch t {
- case NoError, Delegation:
- return Success
- case NameError, NoData:
- return Denial
- case OtherError:
- fallthrough
- default:
- return Error
- }
-}
diff --git a/middleware/pkg/response/typify.go b/middleware/pkg/response/typify.go
deleted file mode 100644
index 7cfaab497..000000000
--- a/middleware/pkg/response/typify.go
+++ /dev/null
@@ -1,146 +0,0 @@
-package response
-
-import (
- "fmt"
- "time"
-
- "github.com/miekg/dns"
-)
-
-// Type is the type of the message.
-type Type int
-
-const (
- // NoError indicates a positive reply
- NoError Type = iota
- // NameError is a NXDOMAIN in header, SOA in auth.
- NameError
- // NoData indicates name found, but not the type: NOERROR in header, SOA in auth.
- NoData
- // Delegation is a msg with a pointer to another nameserver: NOERROR in header, NS in auth, optionally fluff in additional (not checked).
- Delegation
- // Meta indicates a meta message, NOTIFY, or a transfer: qType is IXFR or AXFR.
- Meta
- // Update is an dynamic update message.
- Update
- // OtherError indicates any other error: don't cache these.
- OtherError
-)
-
-var toString = map[Type]string{
- NoError: "NOERROR",
- NameError: "NXDOMAIN",
- NoData: "NODATA",
- Delegation: "DELEGATION",
- Meta: "META",
- Update: "UPDATE",
- OtherError: "OTHERERROR",
-}
-
-func (t Type) String() string { return toString[t] }
-
-// TypeFromString returns the type from the string s. If not type matches
-// the OtherError type and an error are returned.
-func TypeFromString(s string) (Type, error) {
- for t, str := range toString {
- if s == str {
- return t, nil
- }
- }
- return NoError, fmt.Errorf("invalid Type: %s", s)
-}
-
-// Typify classifies a message, it returns the Type.
-func Typify(m *dns.Msg, t time.Time) (Type, *dns.OPT) {
- if m == nil {
- return OtherError, nil
- }
- opt := m.IsEdns0()
- do := false
- if opt != nil {
- do = opt.Do()
- }
-
- if m.Opcode == dns.OpcodeUpdate {
- return Update, opt
- }
-
- // Check transfer and update first
- if m.Opcode == dns.OpcodeNotify {
- return Meta, opt
- }
-
- if len(m.Question) > 0 {
- if m.Question[0].Qtype == dns.TypeAXFR || m.Question[0].Qtype == dns.TypeIXFR {
- return Meta, opt
- }
- }
-
- // If our message contains any expired sigs and we care about that, we should return expired
- if do {
- if expired := typifyExpired(m, t); expired {
- return OtherError, opt
- }
- }
-
- if len(m.Answer) > 0 && m.Rcode == dns.RcodeSuccess {
- return NoError, opt
- }
-
- soa := false
- ns := 0
- for _, r := range m.Ns {
- if r.Header().Rrtype == dns.TypeSOA {
- soa = true
- continue
- }
- if r.Header().Rrtype == dns.TypeNS {
- ns++
- }
- }
-
- // Check length of different sections, and drop stuff that is just to large? TODO(miek).
-
- if soa && m.Rcode == dns.RcodeSuccess {
- return NoData, opt
- }
- if soa && m.Rcode == dns.RcodeNameError {
- return NameError, opt
- }
-
- if ns > 0 && m.Rcode == dns.RcodeSuccess {
- return Delegation, opt
- }
-
- if m.Rcode == dns.RcodeSuccess {
- return NoError, opt
- }
-
- return OtherError, opt
-}
-
-func typifyExpired(m *dns.Msg, t time.Time) bool {
- if expired := typifyExpiredRRSIG(m.Answer, t); expired {
- return true
- }
- if expired := typifyExpiredRRSIG(m.Ns, t); expired {
- return true
- }
- if expired := typifyExpiredRRSIG(m.Extra, t); expired {
- return true
- }
- return false
-}
-
-func typifyExpiredRRSIG(rrs []dns.RR, t time.Time) bool {
- for _, r := range rrs {
- if r.Header().Rrtype != dns.TypeRRSIG {
- continue
- }
- ok := r.(*dns.RRSIG).ValidityPeriod(t)
- if !ok {
- return true
- }
- }
- return false
-}
diff --git a/middleware/pkg/response/typify_test.go b/middleware/pkg/response/typify_test.go
deleted file mode 100644
index 738c6066d..000000000
--- a/middleware/pkg/response/typify_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package response
-
-import (
- "testing"
- "time"
-
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
-)
-
-func TestTypifyNilMsg(t *testing.T) {
- var m *dns.Msg
-
- ty, _ := Typify(m, time.Now().UTC())
- if ty != OtherError {
- t.Errorf("message wrongly typified, expected OtherError, got %s", ty)
- }
-}
-
-func TestTypifyDelegation(t *testing.T) {
- m := delegationMsg()
- mt, _ := Typify(m, time.Now().UTC())
- if mt != Delegation {
- t.Errorf("message is wrongly typified, expected Delegation, got %s", mt)
- }
-}
-
-func TestTypifyRRSIG(t *testing.T) {
- now, _ := time.Parse(time.UnixDate, "Fri Apr 21 10:51:21 BST 2017")
- utc := now.UTC()
-
- m := delegationMsgRRSIGOK()
- if mt, _ := Typify(m, utc); mt != Delegation {
- t.Errorf("message is wrongly typified, expected Delegation, got %s", mt)
- }
-
- // Still a Delegation because EDNS0 OPT DO bool is not set, so we won't check the sigs.
- m = delegationMsgRRSIGFail()
- if mt, _ := Typify(m, utc); mt != Delegation {
- t.Errorf("message is wrongly typified, expected Delegation, got %s", mt)
- }
-
- m = delegationMsgRRSIGFail()
- m = addOpt(m)
- if mt, _ := Typify(m, utc); mt != OtherError {
- t.Errorf("message is wrongly typified, expected OtherError, got %s", mt)
- }
-}
-
-func delegationMsg() *dns.Msg {
- return &dns.Msg{
- Ns: []dns.RR{
- test.NS("miek.nl. 3600 IN NS linode.atoom.net."),
- test.NS("miek.nl. 3600 IN NS ns-ext.nlnetlabs.nl."),
- test.NS("miek.nl. 3600 IN NS omval.tednet.nl."),
- },
- Extra: []dns.RR{
- test.A("omval.tednet.nl. 3600 IN A 185.49.141.42"),
- test.AAAA("omval.tednet.nl. 3600 IN AAAA 2a04:b900:0:100::42"),
- },
- }
-}
-
-func delegationMsgRRSIGOK() *dns.Msg {
- del := delegationMsg()
- del.Ns = append(del.Ns,
- test.RRSIG("miek.nl. 1800 IN RRSIG NS 8 2 1800 20170521031301 20170421031301 12051 miek.nl. PIUu3TKX/sB/N1n1E1yWxHHIcPnc2q6Wq9InShk+5ptRqChqKdZNMLDm gCq+1bQAZ7jGvn2PbwTwE65JzES7T+hEiqR5PU23DsidvZyClbZ9l0xG JtKwgzGXLtUHxp4xv/Plq+rq/7pOG61bNCxRyS7WS7i7QcCCWT1BCcv+ wZ0="),
- )
- return del
-}
-
-func delegationMsgRRSIGFail() *dns.Msg {
- del := delegationMsg()
- del.Ns = append(del.Ns,
- test.RRSIG("miek.nl. 1800 IN RRSIG NS 8 2 1800 20160521031301 20160421031301 12051 miek.nl. PIUu3TKX/sB/N1n1E1yWxHHIcPnc2q6Wq9InShk+5ptRqChqKdZNMLDm gCq+1bQAZ7jGvn2PbwTwE65JzES7T+hEiqR5PU23DsidvZyClbZ9l0xG JtKwgzGXLtUHxp4xv/Plq+rq/7pOG61bNCxRyS7WS7i7QcCCWT1BCcv+ wZ0="),
- )
- return del
-}
-
-func addOpt(m *dns.Msg) *dns.Msg {
- m.Extra = append(m.Extra, test.OPT(4096, true))
- return m
-}
diff --git a/middleware/pkg/singleflight/singleflight.go b/middleware/pkg/singleflight/singleflight.go
deleted file mode 100644
index 365e3ef58..000000000
--- a/middleware/pkg/singleflight/singleflight.go
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-Copyright 2012 Google Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Package singleflight provides a duplicate function call suppression
-// mechanism.
-package singleflight
-
-import "sync"
-
-// call is an in-flight or completed Do call
-type call struct {
- wg sync.WaitGroup
- val interface{}
- err error
-}
-
-// Group represents a class of work and forms a namespace in which
-// units of work can be executed with duplicate suppression.
-type Group struct {
- mu sync.Mutex // protects m
- m map[uint32]*call // lazily initialized
-}
-
-// Do executes and returns the results of the given function, making
-// sure that only one execution is in-flight for a given key at a
-// time. If a duplicate comes in, the duplicate caller waits for the
-// original to complete and receives the same results.
-func (g *Group) Do(key uint32, fn func() (interface{}, error)) (interface{}, error) {
- g.mu.Lock()
- if g.m == nil {
- g.m = make(map[uint32]*call)
- }
- if c, ok := g.m[key]; ok {
- g.mu.Unlock()
- c.wg.Wait()
- return c.val, c.err
- }
- c := new(call)
- c.wg.Add(1)
- g.m[key] = c
- g.mu.Unlock()
-
- c.val, c.err = fn()
- c.wg.Done()
-
- g.mu.Lock()
- delete(g.m, key)
- g.mu.Unlock()
-
- return c.val, c.err
-}
diff --git a/middleware/pkg/singleflight/singleflight_test.go b/middleware/pkg/singleflight/singleflight_test.go
deleted file mode 100644
index d1d406e0b..000000000
--- a/middleware/pkg/singleflight/singleflight_test.go
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-Copyright 2012 Google Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package singleflight
-
-import (
- "errors"
- "fmt"
- "sync"
- "sync/atomic"
- "testing"
- "time"
-)
-
-func TestDo(t *testing.T) {
- var g Group
- v, err := g.Do(1, func() (interface{}, error) {
- return "bar", nil
- })
- if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want {
- t.Errorf("Do = %v; want %v", got, want)
- }
- if err != nil {
- t.Errorf("Do error = %v", err)
- }
-}
-
-func TestDoErr(t *testing.T) {
- var g Group
- someErr := errors.New("Some error")
- v, err := g.Do(1, func() (interface{}, error) {
- return nil, someErr
- })
- if err != someErr {
- t.Errorf("Do error = %v; want someErr", err)
- }
- if v != nil {
- t.Errorf("unexpected non-nil value %#v", v)
- }
-}
-
-func TestDoDupSuppress(t *testing.T) {
- var g Group
- c := make(chan string)
- var calls int32
- fn := func() (interface{}, error) {
- atomic.AddInt32(&calls, 1)
- return <-c, nil
- }
-
- const n = 10
- var wg sync.WaitGroup
- for i := 0; i < n; i++ {
- wg.Add(1)
- go func() {
- v, err := g.Do(1, fn)
- if err != nil {
- t.Errorf("Do error: %v", err)
- }
- if v.(string) != "bar" {
- t.Errorf("got %q; want %q", v, "bar")
- }
- wg.Done()
- }()
- }
- time.Sleep(100 * time.Millisecond) // let goroutines above block
- c <- "bar"
- wg.Wait()
- if got := atomic.LoadInt32(&calls); got != 1 {
- t.Errorf("number of calls = %d; want 1", got)
- }
-}
diff --git a/middleware/pkg/tls/tls.go b/middleware/pkg/tls/tls.go
deleted file mode 100644
index 6fc10dd8e..000000000
--- a/middleware/pkg/tls/tls.go
+++ /dev/null
@@ -1,128 +0,0 @@
-package tls
-
-import (
- "crypto/tls"
- "crypto/x509"
- "fmt"
- "io/ioutil"
- "net"
- "net/http"
- "time"
-)
-
-// NewTLSConfigFromArgs returns a TLS config based upon the passed
-// in list of arguments. Typically these come straight from the
-// Corefile.
-// no args
-// - creates a Config with no cert and using system CAs
-// - use for a client that talks to a server with a public signed cert (CA installed in system)
-// - the client will not be authenticated by the server since there is no cert
-// one arg: the path to CA PEM file
-// - creates a Config with no cert using a specific CA
-// - use for a client that talks to a server with a private signed cert (CA not installed in system)
-// - the client will not be authenticated by the server since there is no cert
-// two args: path to cert PEM file, the path to private key PEM file
-// - creates a Config with a cert, using system CAs to validate the other end
-// - use for:
-// - a server; or,
-// - a client that talks to a server with a public cert and needs certificate-based authentication
-// - the other end will authenticate this end via the provided cert
-// - the cert of the other end will be verified via system CAs
-// three args: path to cert PEM file, path to client private key PEM file, path to CA PEM file
-// - creates a Config with the cert, using specified CA to validate the other end
-// - use for:
-// - a server; or,
-// - a client that talks to a server with a privately signed cert and needs certificate-based
-// authentication
-// - the other end will authenticate this end via the provided cert
-// - this end will verify the other end's cert using the specified CA
-func NewTLSConfigFromArgs(args ...string) (*tls.Config, error) {
- var err error
- var c *tls.Config
- switch len(args) {
- case 0:
- // No client cert, use system CA
- c, err = NewTLSClientConfig("")
- case 1:
- // No client cert, use specified CA
- c, err = NewTLSClientConfig(args[0])
- case 2:
- // Client cert, use system CA
- c, err = NewTLSConfig(args[0], args[1], "")
- case 3:
- // Client cert, use specified CA
- c, err = NewTLSConfig(args[0], args[1], args[2])
- default:
- err = fmt.Errorf("maximum of three arguments allowed for TLS config, found %d", len(args))
- }
- if err != nil {
- return nil, err
- }
- return c, nil
-}
-
-// NewTLSConfig returns a TLS config that includes a certificate
-// Use for server TLS config or when using a client certificate
-// If caPath is empty, system CAs will be used
-func NewTLSConfig(certPath, keyPath, caPath string) (*tls.Config, error) {
- cert, err := tls.LoadX509KeyPair(certPath, keyPath)
- if err != nil {
- return nil, fmt.Errorf("could not load TLS cert: %s", err)
- }
-
- roots, err := loadRoots(caPath)
- if err != nil {
- return nil, err
- }
-
- return &tls.Config{Certificates: []tls.Certificate{cert}, RootCAs: roots}, nil
-}
-
-// NewTLSClientConfig returns a TLS config for a client connection
-// If caPath is empty, system CAs will be used
-func NewTLSClientConfig(caPath string) (*tls.Config, error) {
- roots, err := loadRoots(caPath)
- if err != nil {
- return nil, err
- }
-
- return &tls.Config{RootCAs: roots}, nil
-}
-
-func loadRoots(caPath string) (*x509.CertPool, error) {
- if caPath == "" {
- return nil, nil
- }
-
- roots := x509.NewCertPool()
- pem, err := ioutil.ReadFile(caPath)
- if err != nil {
- return nil, fmt.Errorf("error reading %s: %s", caPath, err)
- }
- ok := roots.AppendCertsFromPEM(pem)
- if !ok {
- return nil, fmt.Errorf("could not read root certs: %s", err)
- }
- return roots, nil
-}
-
-// NewHTTPSTransport returns an HTTP transport configured using tls.Config
-func NewHTTPSTransport(cc *tls.Config) *http.Transport {
- // this seems like a bad idea but was here in the previous version
- if cc != nil {
- cc.InsecureSkipVerify = true
- }
-
- tr := &http.Transport{
- Proxy: http.ProxyFromEnvironment,
- Dial: (&net.Dialer{
- Timeout: 30 * time.Second,
- KeepAlive: 30 * time.Second,
- }).Dial,
- TLSHandshakeTimeout: 10 * time.Second,
- TLSClientConfig: cc,
- MaxIdleConnsPerHost: 25,
- }
-
- return tr
-}
diff --git a/middleware/pkg/tls/tls_test.go b/middleware/pkg/tls/tls_test.go
deleted file mode 100644
index 408469045..000000000
--- a/middleware/pkg/tls/tls_test.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package tls
-
-import (
- "path/filepath"
- "testing"
-
- "github.com/coredns/coredns/middleware/test"
-)
-
-func getPEMFiles(t *testing.T) (rmFunc func(), cert, key, ca string) {
- tempDir, rmFunc, err := test.WritePEMFiles("")
- if err != nil {
- t.Fatalf("Could not write PEM files: %s", err)
- }
-
- cert = filepath.Join(tempDir, "cert.pem")
- key = filepath.Join(tempDir, "key.pem")
- ca = filepath.Join(tempDir, "ca.pem")
-
- return
-}
-
-func TestNewTLSConfig(t *testing.T) {
- rmFunc, cert, key, ca := getPEMFiles(t)
- defer rmFunc()
-
- _, err := NewTLSConfig(cert, key, ca)
- if err != nil {
- t.Errorf("Failed to create TLSConfig: %s", err)
- }
-}
-
-func TestNewTLSClientConfig(t *testing.T) {
- rmFunc, _, _, ca := getPEMFiles(t)
- defer rmFunc()
-
- _, err := NewTLSClientConfig(ca)
- if err != nil {
- t.Errorf("Failed to create TLSConfig: %s", err)
- }
-}
-
-func TestNewTLSConfigFromArgs(t *testing.T) {
- rmFunc, cert, key, ca := getPEMFiles(t)
- defer rmFunc()
-
- _, err := NewTLSConfigFromArgs()
- if err != nil {
- t.Errorf("Failed to create TLSConfig: %s", err)
- }
-
- c, err := NewTLSConfigFromArgs(ca)
- if err != nil {
- t.Errorf("Failed to create TLSConfig: %s", err)
- }
- if c.RootCAs == nil {
- t.Error("RootCAs should not be nil when one arg passed")
- }
-
- c, err = NewTLSConfigFromArgs(cert, key)
- if err != nil {
- t.Errorf("Failed to create TLSConfig: %s", err)
- }
- if c.RootCAs != nil {
- t.Error("RootCAs should be nil when two args passed")
- }
- if len(c.Certificates) != 1 {
- t.Error("Certificates should have a single entry when two args passed")
- }
- args := []string{cert, key, ca}
- c, err = NewTLSConfigFromArgs(args...)
- if err != nil {
- t.Errorf("Failed to create TLSConfig: %s", err)
- }
- if c.RootCAs == nil {
- t.Error("RootCAs should not be nil when three args passed")
- }
- if len(c.Certificates) != 1 {
- t.Error("Certificateis should have a single entry when three args passed")
- }
-}
-
-func TestNewHTTPSTransport(t *testing.T) {
- rmFunc, _, _, ca := getPEMFiles(t)
- defer rmFunc()
-
- cc, err := NewTLSClientConfig(ca)
- if err != nil {
- t.Errorf("Failed to create TLSConfig: %s", err)
- }
-
- tr := NewHTTPSTransport(cc)
- if tr == nil {
- t.Errorf("Failed to create https transport with cc")
- }
-
- tr = NewHTTPSTransport(nil)
- if tr == nil {
- t.Errorf("Failed to create https transport without cc")
- }
-}
diff --git a/middleware/pkg/trace/trace.go b/middleware/pkg/trace/trace.go
deleted file mode 100644
index e4858942b..000000000
--- a/middleware/pkg/trace/trace.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package trace
-
-import (
- "github.com/coredns/coredns/middleware"
- ot "github.com/opentracing/opentracing-go"
-)
-
-// Trace holds the tracer and endpoint info
-type Trace interface {
- middleware.Handler
- Tracer() ot.Tracer
-}
diff --git a/middleware/pprof/README.md b/middleware/pprof/README.md
deleted file mode 100644
index 06a36e442..000000000
--- a/middleware/pprof/README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# pprof
-
-*pprof* publishes runtime profiling data at endpoints under /debug/pprof.
-
-You can visit `/debug/pprof` on your site for an index of the available endpoints. By default it
-will listen on localhost:6053.
-
-> This is a debugging tool. Certain requests (such as collecting execution traces) can be slow. If
-> you use pprof on a live site, consider restricting access or enabling it only temporarily.
-
-For more information, please see [Go's pprof
-documentation](https://golang.org/pkg/net/http/pprof/) and read
-[Profiling Go Programs](https://blog.golang.org/profiling-go-programs).
-
-## Syntax
-
-~~~
-pprof [ADDRESS]
-~~~
-
-If not specified, ADDRESS defaults to localhost:6053.
-
-## Examples
-
-Enable pprof endpoints:
-
-~~~
-pprof
-~~~
-
-Listen on an alternate address:
-
-~~~
-pprof 10.9.8.7:6060
-~~~
-
-Listen on an all addresses on port 6060:
-
-~~~
-pprof :6060
-~~~
diff --git a/middleware/pprof/pprof.go b/middleware/pprof/pprof.go
deleted file mode 100644
index 020776ecf..000000000
--- a/middleware/pprof/pprof.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// Package pprof implement a debug endpoint for getting profiles using the
-// go pprof tooling.
-package pprof
-
-import (
- "log"
- "net"
- "net/http"
- pp "net/http/pprof"
-)
-
-type handler struct {
- addr string
- ln net.Listener
- mux *http.ServeMux
-}
-
-func (h *handler) Startup() error {
- ln, err := net.Listen("tcp", h.addr)
- if err != nil {
- log.Printf("[ERROR] Failed to start pprof handler: %s", err)
- return err
- }
-
- h.ln = ln
-
- h.mux = http.NewServeMux()
- h.mux.HandleFunc(path+"/", pp.Index)
- h.mux.HandleFunc(path+"/cmdline", pp.Cmdline)
- h.mux.HandleFunc(path+"/profile", pp.Profile)
- h.mux.HandleFunc(path+"/symbol", pp.Symbol)
- h.mux.HandleFunc(path+"/trace", pp.Trace)
-
- go func() {
- http.Serve(h.ln, h.mux)
- }()
- return nil
-}
-
-func (h *handler) Shutdown() error {
- if h.ln != nil {
- return h.ln.Close()
- }
- return nil
-}
-
-const (
- path = "/debug/pprof"
-)
diff --git a/middleware/pprof/setup.go b/middleware/pprof/setup.go
deleted file mode 100644
index f94eef710..000000000
--- a/middleware/pprof/setup.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package pprof
-
-import (
- "net"
- "sync"
-
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-const defaultAddr = "localhost:6053"
-
-func init() {
- caddy.RegisterPlugin("pprof", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- found := false
- h := &handler{addr: defaultAddr}
- for c.Next() {
- if found {
- return middleware.Error("pprof", c.Err("pprof can only be specified once"))
- }
- args := c.RemainingArgs()
- if len(args) == 1 {
- h.addr = args[0]
- _, _, e := net.SplitHostPort(h.addr)
- if e != nil {
- return e
- }
- }
- if len(args) > 1 {
- return middleware.Error("pprof", c.ArgErr())
- }
- if c.NextBlock() {
- return middleware.Error("pprof", c.ArgErr())
- }
- found = true
- }
-
- pprofOnce.Do(func() {
- c.OnStartup(h.Startup)
- c.OnShutdown(h.Shutdown)
- })
-
- return nil
-}
-
-var pprofOnce sync.Once
diff --git a/middleware/pprof/setup_test.go b/middleware/pprof/setup_test.go
deleted file mode 100644
index eaa4cb37e..000000000
--- a/middleware/pprof/setup_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package pprof
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestPProf(t *testing.T) {
- tests := []struct {
- input string
- shouldErr bool
- }{
- {`pprof`, false},
- {`pprof 1.2.3.4:1234`, false},
- {`pprof :1234`, false},
- {`pprof {}`, true},
- {`pprof /foo`, true},
- {`pprof {
- a b
- }`, true},
- {`pprof
- pprof`, true},
- }
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- err := setup(c)
- if test.shouldErr && err == nil {
- t.Errorf("Test %v: Expected error but found nil", i)
- } else if !test.shouldErr && err != nil {
- t.Errorf("Test %v: Expected no error but found error: %v", i, err)
- }
- }
-}
diff --git a/middleware/proxy/README.md b/middleware/proxy/README.md
deleted file mode 100644
index 17a43f68e..000000000
--- a/middleware/proxy/README.md
+++ /dev/null
@@ -1,175 +0,0 @@
-# proxy
-
-*proxy* facilitates both a basic reverse proxy and a robust load balancer.
-
-The proxy has support for multiple backends. The load balancing features include multiple policies,
-health checks, and failovers. If all hosts fail their health check the proxy middleware will fail
-back to randomly selecting a target and sending packets to it.
-
-## Syntax
-
-In its most basic form, a simple reverse proxy uses this syntax:
-
-~~~
-proxy FROM TO
-~~~
-
-* **FROM** is the base domain to match for the request to be proxied.
-* **TO** is the destination endpoint to proxy to.
-
-However, advanced features including load balancing can be utilized with an expanded syntax:
-
-~~~
-proxy FROM TO... {
- policy random|least_conn|round_robin
- fail_timeout DURATION
- max_fails INTEGER
- health_check PATH:PORT [DURATION]
- except IGNORED_NAMES...
- spray
- protocol [dns [force_tcp]|https_google [bootstrap ADDRESS...]|grpc [insecure|CACERT|KEY CERT|KEY CERT CACERT]]
-}
-~~~
-
-* **FROM** is the name to match for the request to be proxied.
-* **TO** is the destination endpoint to proxy to. At least one is required, but multiple may be
- specified. **TO** may be an IP:Port pair, or may reference a file in resolv.conf format
-* `policy` is the load balancing policy to use; applies only with multiple backends. May be one of
- random, least_conn, or round_robin. Default is random.
-* `fail_timeout` specifies how long to consider a backend as down after it has failed. While it is
- down, requests will not be routed to that backend. A backend is "down" if CoreDNS fails to
- communicate with it. The default value is 10 seconds ("10s").
-* `max_fails` is the number of failures within fail_timeout that are needed before considering
- a backend to be down. If 0, the backend will never be marked as down. Default is 1.
-* `health_check` will check path (on port) on each backend. If a backend returns a status code of
- 200-399, then that backend is marked healthy for double the healthcheck duration. If it doesn't,
- it is marked as unhealthy and no requests are routed to it. If this option is not provided then
- health checks are disabled. The default duration is 30 seconds ("30s").
-* **IGNORED_NAMES** in `except` is a space-separated list of domains to exclude from proxying.
- Requests that match none of these names will be passed through.
-* `spray` when all backends are unhealthy, randomly pick one to send the traffic to. (This is
- a failsafe.)
-* `protocol` specifies what protocol to use to speak to an upstream, `dns` (the default) is plain
- old DNS, and `https_google` uses `https://dns.google.com` and speaks a JSON DNS dialect. Note when
- using this **TO** will be ignored. The `grpc` option will talk to a server that has implemented
- the [DnsService](https://github.com/coredns/coredns/pb/dns.proto).
- An out-of-tree middleware that implements the server side of this can be found at
- [here](https://github.com/infobloxopen/coredns-grpc).
-
-## Policies
-
-There are three load-balancing policies available:
-* `random` (default) - Randomly select a backend
-* `least_conn` - Select the backend with the fewest active connections
-* `round_robin` - Select the backend in round-robin fashion
-
-All polices implement randomly spraying packets to backend hosts when *no healthy* hosts are
-available. This is to preeempt the case where the healthchecking (as a mechanism) fails.
-
-## Upstream Protocols
-
-Currently `protocol` supports `dns` (i.e., standard DNS over UDP/TCP) and `https_google` (JSON
-payload over HTTPS). Note that with `https_google` the entire transport is encrypted. Only *you* and
-*Google* can see your DNS activity.
-
-* `dns`: uses the standard DNS exchange. You can pass `force_tcp` to make sure that the proxied connection is performed
- over TCP, regardless of the inbound request's protocol.
-* `https_google`: bootstrap **ADDRESS...** is used to (re-)resolve `dns.google.com` to an address to
- connect to. This happens every 300s. If not specified the default is used: 8.8.8.8:53/8.8.4.4:53.
- Note that **TO** is *ignored* when `https_google` is used, as its upstream is defined as
- `dns.google.com`.
-
- Debug queries are enabled by default and currently there is no way to turn them off. When CoreDNS
- receives a debug query (i.e. the name is prefixed with `o-o.debug.`) a TXT record with Comment
- from `dns.google.com` is added. Note this is not always set.
-* `grpc`: options are used to control how the TLS connection is made to the gRPC server.
- * None - No client authentication is used, and the system CAs are used to verify the server certificate.
- * `insecure` - TLS is not used, the connection is made in plaintext (not good in production).
- * **CACERT** - No client authentication is used, and the file **CACERT** is used to verify the server certificate.
- * **KEY** **CERT** - Client authentication is used with the specified key/cert pair. The server
- certificate is verified with the system CAs.
- * **KEY** **CERT** **CACERT** - Client authentication is used with the specified key/cert pair. The
- server certificate is verified using the **CACERT** file.
-
- An out-of-tree middleware that implements the server side of this can be found at
- [here](https://github.com/infobloxopen/coredns-grpc).
-
-## Metrics
-
-If monitoring is enabled (via the *prometheus* directive) then the following metric is exported:
-
-* coredns_proxy_request_count_total{proto, proxy_proto, from}
-
-Where `proxy_proto` is the protocol used (`dns`, `grpc`, or `https_google`) and `from` is **FROM**
-specified in the config, `proto` is the protocol used by the incoming query ("tcp" or "udp").
-
-## Examples
-
-Proxy all requests within example.org. to a backend system:
-
-~~~
-proxy example.org 127.0.0.1:9005
-~~~
-
-Load-balance all requests between three backends (using random policy):
-
-~~~
-proxy . 10.0.0.10:53 10.0.0.11:1053 10.0.0.12
-~~~
-
-Same as above, but round-robin style:
-
-~~~
-proxy . 10.0.0.10:53 10.0.0.11:1053 10.0.0.12 {
- policy round_robin
-}
-~~~
-
-With health checks and proxy headers to pass hostname, IP, and scheme upstream:
-
-~~~
-proxy . 10.0.0.11:53 10.0.0.11:53 10.0.0.12:53 {
- policy round_robin
- health_check /health:8080
-}
-~~~
-
-Proxy everything except requests to miek.nl or example.org
-
-~~~
-proxy . 10.0.0.10:1234 {
- except miek.nl example.org
-}
-~~~
-
-Proxy everything except example.org using the host resolv.conf nameservers:
-
-~~~
-proxy . /etc/resolv.conf {
- except miek.nl example.org
-}
-~~~
-
-Proxy all requests within example.org to Google's dns.google.com.
-
-~~~
-proxy example.org 1.2.3.4:53 {
- protocol https_google
-}
-~~~
-
-Proxy everything with HTTPS to `dns.google.com`, except `example.org`. Then have another proxy in
-another stanza that uses plain DNS to resolve names under `example.org`.
-
-~~~
-. {
- proxy . 1.2.3.4:53 {
- except example.org
- protocol https_google
- }
-}
-
-example.org {
- proxy . 8.8.8.8:53
-}
-~~~
diff --git a/middleware/proxy/dns.go b/middleware/proxy/dns.go
deleted file mode 100644
index 4d8038422..000000000
--- a/middleware/proxy/dns.go
+++ /dev/null
@@ -1,106 +0,0 @@
-package proxy
-
-import (
- "context"
- "net"
- "time"
-
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-type dnsEx struct {
- Timeout time.Duration
- Options
-}
-
-// Options define the options understood by dns.Exchange.
-type Options struct {
- ForceTCP bool // If true use TCP for upstream no matter what
-}
-
-func newDNSEx() *dnsEx {
- return newDNSExWithOption(Options{})
-}
-
-func newDNSExWithOption(opt Options) *dnsEx {
- return &dnsEx{Timeout: defaultTimeout * time.Second, Options: opt}
-}
-
-func (d *dnsEx) Transport() string {
- if d.Options.ForceTCP {
- return "tcp"
- }
-
- // The protocol will be determined by `state.Proto()` during Exchange.
- return ""
-}
-func (d *dnsEx) Protocol() string { return "dns" }
-func (d *dnsEx) OnShutdown(p *Proxy) error { return nil }
-func (d *dnsEx) OnStartup(p *Proxy) error { return nil }
-
-// Exchange implements the Exchanger interface.
-func (d *dnsEx) Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) {
- proto := state.Proto()
- if d.Options.ForceTCP {
- proto = "tcp"
- }
- co, err := net.DialTimeout(proto, addr, d.Timeout)
- if err != nil {
- return nil, err
- }
-
- reply, _, err := d.ExchangeConn(state.Req, co)
-
- co.Close()
-
- if reply != nil && reply.Truncated {
- // Suppress proxy error for truncated responses
- err = nil
- }
-
- if err != nil {
- return nil, err
- }
- // Make sure it fits in the DNS response.
- reply, _ = state.Scrub(reply)
- reply.Compress = true
- reply.Id = state.Req.Id
-
- return reply, nil
-}
-
-func (d *dnsEx) ExchangeConn(m *dns.Msg, co net.Conn) (*dns.Msg, time.Duration, error) {
- start := time.Now()
- r, err := exchange(m, co)
- rtt := time.Since(start)
-
- return r, rtt, err
-}
-
-func exchange(m *dns.Msg, co net.Conn) (*dns.Msg, error) {
- opt := m.IsEdns0()
-
- udpsize := uint16(dns.MinMsgSize)
- // If EDNS0 is used use that for size.
- if opt != nil && opt.UDPSize() >= dns.MinMsgSize {
- udpsize = opt.UDPSize()
- }
-
- dnsco := &dns.Conn{Conn: co, UDPSize: udpsize}
-
- writeDeadline := time.Now().Add(defaultTimeout)
- dnsco.SetWriteDeadline(writeDeadline)
- dnsco.WriteMsg(m)
-
- readDeadline := time.Now().Add(defaultTimeout)
- co.SetReadDeadline(readDeadline)
- r, err := dnsco.ReadMsg()
-
- dnsco.Close()
- if r == nil {
- return nil, err
- }
- return r, err
-}
diff --git a/middleware/proxy/dnstap_test.go b/middleware/proxy/dnstap_test.go
deleted file mode 100644
index b3c31c207..000000000
--- a/middleware/proxy/dnstap_test.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package proxy
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/dnstap/msg"
- "github.com/coredns/coredns/middleware/dnstap/test"
- mwtest "github.com/coredns/coredns/middleware/test"
- "github.com/coredns/coredns/request"
-
- tap "github.com/dnstap/golang-dnstap"
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func testCase(t *testing.T, ex Exchanger, q, r *dns.Msg, datq, datr *msg.Data) {
- tapq := datq.ToOutsideQuery(tap.Message_FORWARDER_QUERY)
- tapr := datr.ToOutsideResponse(tap.Message_FORWARDER_RESPONSE)
- ctx := test.Context{}
- err := toDnstap(&ctx, "10.240.0.1:40212", ex,
- request.Request{W: &mwtest.ResponseWriter{}, Req: q}, r, 0, 0)
- if err != nil {
- t.Fatal(err)
- }
- if len(ctx.Trap) != 2 {
- t.Fatalf("messages: %d", len(ctx.Trap))
- }
- if !test.MsgEqual(ctx.Trap[0], tapq) {
- t.Errorf("want: %v\nhave: %v", tapq, ctx.Trap[0])
- }
- if !test.MsgEqual(ctx.Trap[1], tapr) {
- t.Errorf("want: %v\nhave: %v", tapr, ctx.Trap[1])
- }
-}
-
-func TestDnstap(t *testing.T) {
- q := mwtest.Case{Qname: "example.org", Qtype: dns.TypeA}.Msg()
- r := mwtest.Case{
- Qname: "example.org.", Qtype: dns.TypeA,
- Answer: []dns.RR{
- mwtest.A("example.org. 3600 IN A 10.0.0.1"),
- },
- }.Msg()
- tapq, tapr := test.TestingData(), test.TestingData()
- testCase(t, newDNSEx(), q, r, tapq, tapr)
- tapq.SocketProto = tap.SocketProtocol_TCP
- tapr.SocketProto = tap.SocketProtocol_TCP
- testCase(t, newDNSExWithOption(Options{ForceTCP: true}), q, r, tapq, tapr)
- testCase(t, newGoogle("", []string{"8.8.8.8:53", "8.8.4.4:53"}), q, r, tapq, tapr)
-}
-
-func TestNoDnstap(t *testing.T) {
- err := toDnstap(context.TODO(), "", nil, request.Request{}, nil, 0, 0)
- if err != nil {
- t.Fatal(err)
- }
-}
diff --git a/middleware/proxy/exchanger.go b/middleware/proxy/exchanger.go
deleted file mode 100644
index b98a687e7..000000000
--- a/middleware/proxy/exchanger.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package proxy
-
-import (
- "context"
-
- "github.com/coredns/coredns/request"
- "github.com/miekg/dns"
-)
-
-// Exchanger is an interface that specifies a type implementing a DNS resolver that
-// can use whatever transport it likes.
-type Exchanger interface {
- Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error)
- Protocol() string
-
- // Transport returns the only transport protocol used by this Exchanger or "".
- // If the return value is "", Exchange must use `state.Proto()`.
- Transport() string
-
- OnStartup(*Proxy) error
- OnShutdown(*Proxy) error
-}
diff --git a/middleware/proxy/google.go b/middleware/proxy/google.go
deleted file mode 100644
index b7e605fcb..000000000
--- a/middleware/proxy/google.go
+++ /dev/null
@@ -1,244 +0,0 @@
-package proxy
-
-import (
- "context"
- "crypto/tls"
- "encoding/json"
- "fmt"
- "io/ioutil"
- "log"
- "net"
- "net/http"
- "net/url"
- "sync/atomic"
- "time"
-
- "github.com/coredns/coredns/middleware/pkg/healthcheck"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-type google struct {
- client *http.Client
-
- endpoint string // Name to resolve via 'bootstrapProxy'
-
- bootstrapProxy Proxy
- quit chan bool
-}
-
-func newGoogle(endpoint string, bootstrap []string) *google {
- if endpoint == "" {
- endpoint = ghost
- }
- tls := &tls.Config{ServerName: endpoint}
- client := &http.Client{
- Timeout: time.Second * defaultTimeout,
- Transport: &http.Transport{TLSClientConfig: tls},
- }
-
- boot := NewLookup(bootstrap)
-
- return &google{client: client, endpoint: dns.Fqdn(endpoint), bootstrapProxy: boot, quit: make(chan bool)}
-}
-
-func (g *google) Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) {
- v := url.Values{}
-
- v.Set("name", state.Name())
- v.Set("type", fmt.Sprintf("%d", state.QType()))
-
- buf, backendErr := g.exchangeJSON(addr, v.Encode())
-
- if backendErr == nil {
- gm := new(googleMsg)
- if err := json.Unmarshal(buf, gm); err != nil {
- return nil, err
- }
-
- m, err := toMsg(gm)
- if err != nil {
- return nil, err
- }
-
- m.Id = state.Req.Id
- return m, nil
- }
-
- log.Printf("[WARNING] Failed to connect to HTTPS backend %q: %s", g.endpoint, backendErr)
- return nil, backendErr
-}
-
-func (g *google) exchangeJSON(addr, json string) ([]byte, error) {
- url := "https://" + addr + "/resolve?" + json
- req, err := http.NewRequest("GET", url, nil)
- if err != nil {
- return nil, err
- }
-
- req.Host = g.endpoint // TODO(miek): works with the extra dot at the end?
-
- resp, err := g.client.Do(req)
- if err != nil {
- return nil, err
- }
-
- buf, err := ioutil.ReadAll(resp.Body)
- resp.Body.Close()
- if err != nil {
- return nil, err
- }
-
- if resp.StatusCode != 200 {
- return nil, fmt.Errorf("failed to get 200 status code, got %d", resp.StatusCode)
- }
-
- return buf, nil
-}
-
-func (g *google) Transport() string { return "tcp" }
-func (g *google) Protocol() string { return "https_google" }
-
-func (g *google) OnShutdown(p *Proxy) error {
- g.quit <- true
- return nil
-}
-
-func (g *google) OnStartup(p *Proxy) error {
- // We fake a state because normally the proxy is called after we already got a incoming query.
- // This is a non-edns0, udp request to g.endpoint.
- req := new(dns.Msg)
- req.SetQuestion(g.endpoint, dns.TypeA)
- state := request.Request{W: new(fakeBootWriter), Req: req}
-
- if len(*p.Upstreams) == 0 {
- return fmt.Errorf("no upstreams defined")
- }
-
- oldUpstream := (*p.Upstreams)[0]
-
- log.Printf("[INFO] Bootstrapping A records %q", g.endpoint)
-
- new, err := g.bootstrapProxy.Lookup(state, g.endpoint, dns.TypeA)
- if err != nil {
- log.Printf("[WARNING] Failed to bootstrap A records %q: %s", g.endpoint, err)
- } else {
- addrs, err1 := extractAnswer(new)
- if err1 != nil {
- log.Printf("[WARNING] Failed to bootstrap A records %q: %s", g.endpoint, err1)
- } else {
-
- up := newUpstream(addrs, oldUpstream.(*staticUpstream))
- p.Upstreams = &[]Upstream{up}
-
- log.Printf("[INFO] Bootstrapping A records %q found: %v", g.endpoint, addrs)
- }
- }
-
- go func() {
- tick := time.NewTicker(120 * time.Second)
-
- for {
- select {
- case <-tick.C:
-
- log.Printf("[INFO] Resolving A records %q", g.endpoint)
-
- new, err := g.bootstrapProxy.Lookup(state, g.endpoint, dns.TypeA)
- if err != nil {
- log.Printf("[WARNING] Failed to resolve A records %q: %s", g.endpoint, err)
- continue
- }
-
- addrs, err1 := extractAnswer(new)
- if err1 != nil {
- log.Printf("[WARNING] Failed to resolve A records %q: %s", g.endpoint, err1)
- continue
- }
-
- up := newUpstream(addrs, oldUpstream.(*staticUpstream))
- p.Upstreams = &[]Upstream{up}
-
- log.Printf("[INFO] Resolving A records %q found: %v", g.endpoint, addrs)
-
- case <-g.quit:
- return
- }
- }
- }()
-
- return nil
-}
-
-func extractAnswer(m *dns.Msg) ([]string, error) {
- if len(m.Answer) == 0 {
- return nil, fmt.Errorf("no answer section in response")
- }
- ret := []string{}
- for _, an := range m.Answer {
- if a, ok := an.(*dns.A); ok {
- ret = append(ret, net.JoinHostPort(a.A.String(), "443"))
- }
- }
- if len(ret) > 0 {
- return ret, nil
- }
-
- return nil, fmt.Errorf("no address records in answer section")
-}
-
-// newUpstream returns an upstream initialized with hosts.
-func newUpstream(hosts []string, old *staticUpstream) Upstream {
- upstream := &staticUpstream{
- from: old.from,
- HealthCheck: healthcheck.HealthCheck{
- FailTimeout: 10 * time.Second,
- MaxFails: 3,
- Future: 60 * time.Second,
- },
- ex: old.ex,
- WithoutPathPrefix: old.WithoutPathPrefix,
- IgnoredSubDomains: old.IgnoredSubDomains,
- }
-
- upstream.Hosts = make([]*healthcheck.UpstreamHost, len(hosts))
- for i, h := range hosts {
- uh := &healthcheck.UpstreamHost{
- Name: h,
- Conns: 0,
- Fails: 0,
- FailTimeout: upstream.FailTimeout,
-
- CheckDown: func(upstream *staticUpstream) healthcheck.UpstreamHostDownFunc {
- return func(uh *healthcheck.UpstreamHost) bool {
-
- down := false
-
- uh.CheckMu.Lock()
- until := uh.OkUntil
- uh.CheckMu.Unlock()
-
- if !until.IsZero() && time.Now().After(until) {
- down = true
- }
-
- fails := atomic.LoadInt32(&uh.Fails)
- if fails >= upstream.MaxFails && upstream.MaxFails != 0 {
- down = true
- }
- return down
- }
- }(upstream),
- WithoutPathPrefix: upstream.WithoutPathPrefix,
- }
-
- upstream.Hosts[i] = uh
- }
- return upstream
-}
-
-const (
- // Default endpoint for this service.
- ghost = "dns.google.com."
-)
diff --git a/middleware/proxy/google_rr.go b/middleware/proxy/google_rr.go
deleted file mode 100644
index 3b9233b7b..000000000
--- a/middleware/proxy/google_rr.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package proxy
-
-import (
- "fmt"
-
- "github.com/miekg/dns"
-)
-
-// toMsg converts a googleMsg into the dns message.
-func toMsg(g *googleMsg) (*dns.Msg, error) {
- m := new(dns.Msg)
- m.Response = true
- m.Rcode = g.Status
- m.Truncated = g.TC
- m.RecursionDesired = g.RD
- m.RecursionAvailable = g.RA
- m.AuthenticatedData = g.AD
- m.CheckingDisabled = g.CD
-
- m.Question = make([]dns.Question, 1)
- m.Answer = make([]dns.RR, len(g.Answer))
- m.Ns = make([]dns.RR, len(g.Authority))
- m.Extra = make([]dns.RR, len(g.Additional))
-
- m.Question[0] = dns.Question{Name: g.Question[0].Name, Qtype: g.Question[0].Type, Qclass: dns.ClassINET}
-
- var err error
- for i := 0; i < len(m.Answer); i++ {
- m.Answer[i], err = toRR(g.Answer[i])
- if err != nil {
- return nil, err
- }
- }
- for i := 0; i < len(m.Ns); i++ {
- m.Ns[i], err = toRR(g.Authority[i])
- if err != nil {
- return nil, err
- }
- }
- for i := 0; i < len(m.Extra); i++ {
- m.Extra[i], err = toRR(g.Additional[i])
- if err != nil {
- return nil, err
- }
- }
-
- return m, nil
-}
-
-// toRR transforms a "google" RR to a dns.RR.
-func toRR(g googleRR) (dns.RR, error) {
- typ, ok := dns.TypeToString[g.Type]
- if !ok {
- return nil, fmt.Errorf("failed to convert type %q", g.Type)
- }
-
- str := fmt.Sprintf("%s %d %s %s", g.Name, g.TTL, typ, g.Data)
- rr, err := dns.NewRR(str)
- if err != nil {
- return nil, fmt.Errorf("failed to parse %q: %s", str, err)
- }
- return rr, nil
-}
-
-// googleRR represents a dns.RR in another form.
-type googleRR struct {
- Name string
- Type uint16
- TTL uint32
- Data string
-}
-
-// googleMsg is a JSON representation of the dns.Msg.
-type googleMsg struct {
- Status int
- TC bool
- RD bool
- RA bool
- AD bool
- CD bool
- Question []struct {
- Name string
- Type uint16
- }
- Answer []googleRR
- Authority []googleRR
- Additional []googleRR
- Comment string
-}
diff --git a/middleware/proxy/google_test.go b/middleware/proxy/google_test.go
deleted file mode 100644
index 1ce591664..000000000
--- a/middleware/proxy/google_test.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package proxy
-
-// TODO(miek):
-// Test cert failures - put those in SERVFAIL messages, but attach error code in TXT
-// Test connecting to a a bad host.
diff --git a/middleware/proxy/grpc.go b/middleware/proxy/grpc.go
deleted file mode 100644
index 8aabf0eb0..000000000
--- a/middleware/proxy/grpc.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package proxy
-
-import (
- "context"
- "crypto/tls"
- "log"
-
- "github.com/coredns/coredns/middleware/pkg/trace"
- "github.com/coredns/coredns/pb"
- "github.com/coredns/coredns/request"
-
- "github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
- "github.com/miekg/dns"
- opentracing "github.com/opentracing/opentracing-go"
- "google.golang.org/grpc"
- "google.golang.org/grpc/credentials"
-)
-
-type grpcClient struct {
- dialOpts []grpc.DialOption
- clients map[string]pb.DnsServiceClient
- conns []*grpc.ClientConn
- upstream *staticUpstream
-}
-
-func newGrpcClient(tls *tls.Config, u *staticUpstream) *grpcClient {
- g := &grpcClient{upstream: u}
-
- if tls == nil {
- g.dialOpts = append(g.dialOpts, grpc.WithInsecure())
- } else {
- g.dialOpts = append(g.dialOpts, grpc.WithTransportCredentials(credentials.NewTLS(tls)))
- }
- g.clients = map[string]pb.DnsServiceClient{}
-
- return g
-}
-
-func (g *grpcClient) Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) {
- msg, err := state.Req.Pack()
- if err != nil {
- return nil, err
- }
-
- reply, err := g.clients[addr].Query(ctx, &pb.DnsPacket{Msg: msg})
- if err != nil {
- return nil, err
- }
- d := new(dns.Msg)
- err = d.Unpack(reply.Msg)
- if err != nil {
- return nil, err
- }
- return d, nil
-}
-
-func (g *grpcClient) Transport() string { return "tcp" }
-
-func (g *grpcClient) Protocol() string { return "grpc" }
-
-func (g *grpcClient) OnShutdown(p *Proxy) error {
- g.clients = map[string]pb.DnsServiceClient{}
- for i, conn := range g.conns {
- err := conn.Close()
- if err != nil {
- log.Printf("[WARNING] Error closing connection %d: %s\n", i, err)
- }
- }
- g.conns = []*grpc.ClientConn{}
- return nil
-}
-
-func (g *grpcClient) OnStartup(p *Proxy) error {
- dialOpts := g.dialOpts
- if p.Trace != nil {
- if t, ok := p.Trace.(trace.Trace); ok {
- onlyIfParent := func(parentSpanCtx opentracing.SpanContext, method string, req, resp interface{}) bool {
- return parentSpanCtx != nil
- }
- intercept := otgrpc.OpenTracingClientInterceptor(t.Tracer(), otgrpc.IncludingSpans(onlyIfParent))
- dialOpts = append(dialOpts, grpc.WithUnaryInterceptor(intercept))
- } else {
- log.Printf("[WARNING] Wrong type for trace middleware reference: %s", p.Trace)
- }
- }
- for _, host := range g.upstream.Hosts {
- conn, err := grpc.Dial(host.Name, dialOpts...)
- if err != nil {
- log.Printf("[WARNING] Skipping gRPC host '%s' due to Dial error: %s\n", host.Name, err)
- } else {
- g.clients[host.Name] = pb.NewDnsServiceClient(conn)
- g.conns = append(g.conns, conn)
- }
- }
- return nil
-}
diff --git a/middleware/proxy/grpc_test.go b/middleware/proxy/grpc_test.go
deleted file mode 100644
index dcde7cc0e..000000000
--- a/middleware/proxy/grpc_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package proxy
-
-import (
- "testing"
- "time"
-
- "github.com/coredns/coredns/middleware/pkg/healthcheck"
-
- "google.golang.org/grpc/grpclog"
-)
-
-func pool() []*healthcheck.UpstreamHost {
- return []*healthcheck.UpstreamHost{
- {
- Name: "localhost:10053",
- },
- {
- Name: "localhost:10054",
- },
- }
-}
-
-func TestStartupShutdown(t *testing.T) {
- grpclog.SetLogger(discard{})
-
- upstream := &staticUpstream{
- from: ".",
- HealthCheck: healthcheck.HealthCheck{
- Hosts: pool(),
- FailTimeout: 10 * time.Second,
- Future: 60 * time.Second,
- MaxFails: 1,
- },
- }
- g := newGrpcClient(nil, upstream)
- upstream.ex = g
-
- p := &Proxy{}
- p.Upstreams = &[]Upstream{upstream}
-
- err := g.OnStartup(p)
- if err != nil {
- t.Errorf("Error starting grpc client exchanger: %s", err)
- return
- }
- if len(g.clients) != len(pool()) {
- t.Errorf("Expected %d grpc clients but found %d", len(pool()), len(g.clients))
- }
-
- err = g.OnShutdown(p)
- if err != nil {
- t.Errorf("Error stopping grpc client exchanger: %s", err)
- return
- }
- if len(g.clients) != 0 {
- t.Errorf("Shutdown didn't remove clients, found %d", len(g.clients))
- }
- if len(g.conns) != 0 {
- t.Errorf("Shutdown didn't remove conns, found %d", len(g.conns))
- }
-}
-
-// discard is a Logger that outputs nothing.
-type discard struct{}
-
-func (d discard) Fatal(args ...interface{}) {}
-func (d discard) Fatalf(format string, args ...interface{}) {}
-func (d discard) Fatalln(args ...interface{}) {}
-func (d discard) Print(args ...interface{}) {}
-func (d discard) Printf(format string, args ...interface{}) {}
-func (d discard) Println(args ...interface{}) {}
diff --git a/middleware/proxy/lookup.go b/middleware/proxy/lookup.go
deleted file mode 100644
index 1963e7dbd..000000000
--- a/middleware/proxy/lookup.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package proxy
-
-// functions other middleware might want to use to do lookup in the same style as the proxy.
-
-import (
- "context"
- "fmt"
- "sync/atomic"
- "time"
-
- "github.com/coredns/coredns/middleware/pkg/healthcheck"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
-)
-
-// NewLookup create a new proxy with the hosts in host and a Random policy.
-func NewLookup(hosts []string) Proxy { return NewLookupWithOption(hosts, Options{}) }
-
-// NewLookupWithOption process creates a simple round robin forward with potentially forced proto for upstream.
-func NewLookupWithOption(hosts []string, opts Options) Proxy {
- p := Proxy{Next: nil}
-
- // TODO(miek): this needs to be unified with upstream.go's NewStaticUpstreams, caddy uses NewHost
- // we should copy/make something similar.
- upstream := &staticUpstream{
- from: ".",
- HealthCheck: healthcheck.HealthCheck{
- FailTimeout: 10 * time.Second,
- MaxFails: 3, // TODO(miek): disable error checking for simple lookups?
- Future: 60 * time.Second,
- },
- ex: newDNSExWithOption(opts),
- }
- upstream.Hosts = make([]*healthcheck.UpstreamHost, len(hosts))
-
- for i, host := range hosts {
- uh := &healthcheck.UpstreamHost{
- Name: host,
- Conns: 0,
- Fails: 0,
- FailTimeout: upstream.FailTimeout,
-
- CheckDown: func(upstream *staticUpstream) healthcheck.UpstreamHostDownFunc {
- return func(uh *healthcheck.UpstreamHost) bool {
-
- down := false
-
- uh.CheckMu.Lock()
- until := uh.OkUntil
- uh.CheckMu.Unlock()
-
- if !until.IsZero() && time.Now().After(until) {
- down = true
- }
-
- fails := atomic.LoadInt32(&uh.Fails)
- if fails >= upstream.MaxFails && upstream.MaxFails != 0 {
- down = true
- }
- return down
- }
- }(upstream),
- WithoutPathPrefix: upstream.WithoutPathPrefix,
- }
-
- upstream.Hosts[i] = uh
- }
- p.Upstreams = &[]Upstream{upstream}
- return p
-}
-
-// Lookup will use name and type to forge a new message and will send that upstream. It will
-// set any EDNS0 options correctly so that downstream will be able to process the reply.
-func (p Proxy) Lookup(state request.Request, name string, typ uint16) (*dns.Msg, error) {
- req := new(dns.Msg)
- req.SetQuestion(name, typ)
- state.SizeAndDo(req)
-
- state2 := request.Request{W: state.W, Req: req}
-
- return p.lookup(state2)
-}
-
-// Forward forward the request in state as-is. Unlike Lookup that adds EDNS0 suffix to the message.
-func (p Proxy) Forward(state request.Request) (*dns.Msg, error) {
- return p.lookup(state)
-}
-
-func (p Proxy) lookup(state request.Request) (*dns.Msg, error) {
- upstream := p.match(state)
- if upstream == nil {
- return nil, errInvalidDomain
- }
- for {
- start := time.Now()
- reply := new(dns.Msg)
- var backendErr error
-
- // Since Select() should give us "up" hosts, keep retrying
- // hosts until timeout (or until we get a nil host).
- for time.Since(start) < tryDuration {
- host := upstream.Select()
- if host == nil {
- return nil, fmt.Errorf("%s: %s", errUnreachable, "no upstream host")
- }
-
- // duplicated from proxy.go, but with a twist, we don't write the
- // reply back to the client, we return it and there is no monitoring.
-
- atomic.AddInt64(&host.Conns, 1)
-
- reply, backendErr = upstream.Exchanger().Exchange(context.TODO(), host.Name, state)
-
- atomic.AddInt64(&host.Conns, -1)
-
- if backendErr == nil {
- return reply, nil
- }
- timeout := host.FailTimeout
- if timeout == 0 {
- timeout = 10 * time.Second
- }
- atomic.AddInt32(&host.Fails, 1)
- go func(host *healthcheck.UpstreamHost, timeout time.Duration) {
- time.Sleep(timeout)
- atomic.AddInt32(&host.Fails, -1)
- }(host, timeout)
- }
- return nil, fmt.Errorf("%s: %s", errUnreachable, backendErr)
- }
-}
diff --git a/middleware/proxy/metrics.go b/middleware/proxy/metrics.go
deleted file mode 100644
index e9bb48d6f..000000000
--- a/middleware/proxy/metrics.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package proxy
-
-import (
- "sync"
-
- "github.com/coredns/coredns/middleware"
-
- "github.com/prometheus/client_golang/prometheus"
-)
-
-// Metrics the proxy middleware exports.
-var (
- RequestDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
- Namespace: middleware.Namespace,
- Subsystem: "proxy",
- Name: "request_duration_milliseconds",
- Buckets: append(prometheus.DefBuckets, []float64{50, 100, 200, 500, 1000, 2000, 3000, 4000, 5000, 10000}...),
- Help: "Histogram of the time (in milliseconds) each request took.",
- }, []string{"proto", "proxy_proto", "from"})
-)
-
-// OnStartupMetrics sets up the metrics on startup. This is done for all proxy protocols.
-func OnStartupMetrics() error {
- metricsOnce.Do(func() {
- prometheus.MustRegister(RequestDuration)
- })
- return nil
-}
-
-var metricsOnce sync.Once
diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go
deleted file mode 100644
index 2a3c8002d..000000000
--- a/middleware/proxy/proxy.go
+++ /dev/null
@@ -1,195 +0,0 @@
-// Package proxy is middleware that proxies requests.
-package proxy
-
-import (
- "errors"
- "fmt"
- "sync/atomic"
- "time"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/dnstap"
- "github.com/coredns/coredns/middleware/dnstap/msg"
- "github.com/coredns/coredns/middleware/pkg/healthcheck"
- "github.com/coredns/coredns/request"
-
- tap "github.com/dnstap/golang-dnstap"
- "github.com/miekg/dns"
- ot "github.com/opentracing/opentracing-go"
- "golang.org/x/net/context"
-)
-
-var (
- errUnreachable = errors.New("unreachable backend")
- errInvalidProtocol = errors.New("invalid protocol")
- errInvalidDomain = errors.New("invalid path for proxy")
-)
-
-// Proxy represents a middleware instance that can proxy requests to another (DNS) server.
-type Proxy struct {
- Next middleware.Handler
-
- // Upstreams is a pointer to a slice, so we can update the upstream (used for Google)
- // midway.
-
- Upstreams *[]Upstream
-
- // Trace is the Trace middleware, if it is installed
- // This is used by the grpc exchanger to trace through the grpc calls
- Trace middleware.Handler
-}
-
-// Upstream manages a pool of proxy upstream hosts. Select should return a
-// suitable upstream host, or nil if no such hosts are available.
-type Upstream interface {
- // The domain name this upstream host should be routed on.
- From() string
- // Selects an upstream host to be routed to.
- Select() *healthcheck.UpstreamHost
- // Checks if subpdomain is not an ignored.
- IsAllowedDomain(string) bool
- // Exchanger returns the exchanger to be used for this upstream.
- Exchanger() Exchanger
- // Stops the upstream from proxying requests to shutdown goroutines cleanly.
- Stop() error
-}
-
-// tryDuration is how long to try upstream hosts; failures result in
-// immediate retries until this duration ends or we get a nil host.
-var tryDuration = 60 * time.Second
-
-// ServeDNS satisfies the middleware.Handler interface.
-func (p Proxy) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- var span, child ot.Span
- span = ot.SpanFromContext(ctx)
- state := request.Request{W: w, Req: r}
-
- upstream := p.match(state)
- if upstream == nil {
- return middleware.NextOrFailure(p.Name(), p.Next, ctx, w, r)
- }
-
- for {
- start := time.Now()
- reply := new(dns.Msg)
- var backendErr error
-
- // Since Select() should give us "up" hosts, keep retrying
- // hosts until timeout (or until we get a nil host).
- for time.Since(start) < tryDuration {
- host := upstream.Select()
- if host == nil {
-
- RequestDuration.WithLabelValues(state.Proto(), upstream.Exchanger().Protocol(), upstream.From()).Observe(float64(time.Since(start) / time.Millisecond))
-
- return dns.RcodeServerFailure, fmt.Errorf("%s: %s", errUnreachable, "no upstream host")
- }
-
- if span != nil {
- child = span.Tracer().StartSpan("exchange", ot.ChildOf(span.Context()))
- ctx = ot.ContextWithSpan(ctx, child)
- }
-
- atomic.AddInt64(&host.Conns, 1)
- queryEpoch := msg.Epoch()
-
- reply, backendErr = upstream.Exchanger().Exchange(ctx, host.Name, state)
-
- respEpoch := msg.Epoch()
- atomic.AddInt64(&host.Conns, -1)
-
- if child != nil {
- child.Finish()
- }
-
- taperr := toDnstap(ctx, host.Name, upstream.Exchanger(), state, reply, queryEpoch, respEpoch)
-
- if backendErr == nil {
- w.WriteMsg(reply)
-
- RequestDuration.WithLabelValues(state.Proto(), upstream.Exchanger().Protocol(), upstream.From()).Observe(float64(time.Since(start) / time.Millisecond))
-
- return 0, taperr
- }
-
- timeout := host.FailTimeout
- if timeout == 0 {
- timeout = 10 * time.Second
- }
- atomic.AddInt32(&host.Fails, 1)
- go func(host *healthcheck.UpstreamHost, timeout time.Duration) {
- time.Sleep(timeout)
- atomic.AddInt32(&host.Fails, -1)
- }(host, timeout)
- }
-
- RequestDuration.WithLabelValues(state.Proto(), upstream.Exchanger().Protocol(), upstream.From()).Observe(float64(time.Since(start) / time.Millisecond))
-
- return dns.RcodeServerFailure, fmt.Errorf("%s: %s", errUnreachable, backendErr)
- }
-}
-
-func (p Proxy) match(state request.Request) (u Upstream) {
- if p.Upstreams == nil {
- return nil
- }
-
- longestMatch := 0
- for _, upstream := range *p.Upstreams {
- from := upstream.From()
-
- if !middleware.Name(from).Matches(state.Name()) || !upstream.IsAllowedDomain(state.Name()) {
- continue
- }
-
- if lf := len(from); lf > longestMatch {
- longestMatch = lf
- u = upstream
- }
- }
- return u
-
-}
-
-// Name implements the Handler interface.
-func (p Proxy) Name() string { return "proxy" }
-
-// defaultTimeout is the default networking timeout for DNS requests.
-const defaultTimeout = 5 * time.Second
-
-func toDnstap(ctx context.Context, host string, ex Exchanger, state request.Request, reply *dns.Msg, queryEpoch, respEpoch uint64) (err error) {
- if tapper := dnstap.TapperFromContext(ctx); tapper != nil {
- // Query
- b := tapper.TapBuilder()
- b.TimeSec = queryEpoch
- if err = b.HostPort(host); err != nil {
- return
- }
- t := ex.Transport()
- if t == "" {
- t = state.Proto()
- }
- if t == "tcp" {
- b.SocketProto = tap.SocketProtocol_TCP
- } else {
- b.SocketProto = tap.SocketProtocol_UDP
- }
- if err = b.Msg(state.Req); err != nil {
- return
- }
- err = tapper.TapMessage(b.ToOutsideQuery(tap.Message_FORWARDER_QUERY))
- if err != nil {
- return
- }
-
- // Response
- if reply != nil {
- b.TimeSec = respEpoch
- if err = b.Msg(reply); err != nil {
- return
- }
- err = tapper.TapMessage(b.ToOutsideResponse(tap.Message_FORWARDER_RESPONSE))
- }
- }
- return
-}
diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go
deleted file mode 100644
index b0cb9c3cb..000000000
--- a/middleware/proxy/proxy_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-package proxy
-
-import (
- "fmt"
- "net/http"
- "net/http/httptest"
- "strings"
- "sync/atomic"
- "testing"
- "time"
-
- "github.com/mholt/caddy/caddyfile"
-)
-
-func TestStop(t *testing.T) {
- config := "proxy . %s {\n health_check /healthcheck:%s %dms \n}"
- tests := []struct {
- name string
- intervalInMilliseconds int
- numHealthcheckIntervals int
- }{
- {
- "No Healthchecks After Stop - 5ms, 1 intervals",
- 5,
- 1,
- },
- {
- "No Healthchecks After Stop - 5ms, 2 intervals",
- 5,
- 2,
- },
- {
- "No Healthchecks After Stop - 5ms, 3 intervals",
- 5,
- 3,
- },
- }
-
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
-
- // Set up proxy.
- var counter int64
- backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- r.Body.Close()
- atomic.AddInt64(&counter, 1)
- }))
-
- defer backend.Close()
-
- port := backend.URL[17:] // Remove all crap up to the port
- back := backend.URL[7:] // Remove http://
- c := caddyfile.NewDispenser("Testfile", strings.NewReader(fmt.Sprintf(config, back, port, test.intervalInMilliseconds)))
- upstreams, err := NewStaticUpstreams(&c)
- if err != nil {
- t.Error("Expected no error. Got:", err.Error())
- }
-
- // Give some time for healthchecks to hit the server.
- time.Sleep(time.Duration(test.intervalInMilliseconds*test.numHealthcheckIntervals) * time.Millisecond)
-
- for _, upstream := range upstreams {
- if err := upstream.Stop(); err != nil {
- t.Error("Expected no error stopping upstream. Got: ", err.Error())
- }
- }
-
- counterValueAfterShutdown := atomic.LoadInt64(&counter)
-
- // Give some time to see if healthchecks are still hitting the server.
- time.Sleep(time.Duration(test.intervalInMilliseconds*test.numHealthcheckIntervals) * time.Millisecond)
-
- if counterValueAfterShutdown == 0 {
- t.Error("Expected healthchecks to hit test server. Got no healthchecks.")
- }
-
- // health checks are in a go routine now, so one may well occur after we shutdown,
- // but we only ever expect one more
- counterValueAfterWaiting := atomic.LoadInt64(&counter)
- if counterValueAfterWaiting > (counterValueAfterShutdown + 1) {
- t.Errorf("Expected no more healthchecks after shutdown. Got: %d healthchecks after shutdown", counterValueAfterWaiting-counterValueAfterShutdown)
- }
-
- })
-
- }
-}
diff --git a/middleware/proxy/response.go b/middleware/proxy/response.go
deleted file mode 100644
index 2ad553c41..000000000
--- a/middleware/proxy/response.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package proxy
-
-import (
- "net"
-
- "github.com/miekg/dns"
-)
-
-type fakeBootWriter struct {
- dns.ResponseWriter
-}
-
-func (w *fakeBootWriter) LocalAddr() net.Addr {
- local := net.ParseIP("127.0.0.1")
- return &net.UDPAddr{IP: local, Port: 53} // Port is not used here
-}
-
-func (w *fakeBootWriter) RemoteAddr() net.Addr {
- remote := net.ParseIP("8.8.8.8")
- return &net.UDPAddr{IP: remote, Port: 53} // Port is not used here
-}
diff --git a/middleware/proxy/setup.go b/middleware/proxy/setup.go
deleted file mode 100644
index d55065734..000000000
--- a/middleware/proxy/setup.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package proxy
-
-import (
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("proxy", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- upstreams, err := NewStaticUpstreams(&c.Dispenser)
- if err != nil {
- return middleware.Error("proxy", err)
- }
-
- t := dnsserver.GetConfig(c).Handler("trace")
- P := &Proxy{Trace: t}
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- P.Next = next
- P.Upstreams = &upstreams
- return P
- })
-
- c.OnStartup(OnStartupMetrics)
-
- for i := range upstreams {
- u := upstreams[i]
- c.OnStartup(func() error {
- return u.Exchanger().OnStartup(P)
- })
- c.OnShutdown(func() error {
- return u.Exchanger().OnShutdown(P)
- })
- // Register shutdown handlers.
- c.OnShutdown(u.Stop)
- }
-
- return nil
-}
diff --git a/middleware/proxy/upstream.go b/middleware/proxy/upstream.go
deleted file mode 100644
index 677b8e2fc..000000000
--- a/middleware/proxy/upstream.go
+++ /dev/null
@@ -1,234 +0,0 @@
-package proxy
-
-import (
- "fmt"
- "net"
- "strconv"
- "sync/atomic"
- "time"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/middleware/pkg/healthcheck"
- "github.com/coredns/coredns/middleware/pkg/tls"
- "github.com/mholt/caddy/caddyfile"
- "github.com/miekg/dns"
-)
-
-type staticUpstream struct {
- from string
-
- healthcheck.HealthCheck
-
- WithoutPathPrefix string
- IgnoredSubDomains []string
- ex Exchanger
-}
-
-// NewStaticUpstreams parses the configuration input and sets up
-// static upstreams for the proxy middleware.
-func NewStaticUpstreams(c *caddyfile.Dispenser) ([]Upstream, error) {
- var upstreams []Upstream
- for c.Next() {
- upstream := &staticUpstream{
- from: ".",
- HealthCheck: healthcheck.HealthCheck{
- FailTimeout: 10 * time.Second,
- MaxFails: 1,
- Future: 60 * time.Second,
- },
- ex: newDNSEx(),
- }
-
- if !c.Args(&upstream.from) {
- return upstreams, c.ArgErr()
- }
- upstream.from = middleware.Host(upstream.from).Normalize()
-
- to := c.RemainingArgs()
- if len(to) == 0 {
- return upstreams, c.ArgErr()
- }
-
- // process the host list, substituting in any nameservers in files
- toHosts, err := dnsutil.ParseHostPortOrFile(to...)
- if err != nil {
- return upstreams, err
- }
-
- for c.NextBlock() {
- if err := parseBlock(c, upstream); err != nil {
- return upstreams, err
- }
- }
-
- upstream.Hosts = make([]*healthcheck.UpstreamHost, len(toHosts))
- for i, host := range toHosts {
- uh := &healthcheck.UpstreamHost{
- Name: host,
- Conns: 0,
- Fails: 0,
- FailTimeout: upstream.FailTimeout,
-
- CheckDown: func(upstream *staticUpstream) healthcheck.UpstreamHostDownFunc {
- return func(uh *healthcheck.UpstreamHost) bool {
-
- down := false
-
- uh.CheckMu.Lock()
- until := uh.OkUntil
- uh.CheckMu.Unlock()
-
- if !until.IsZero() && time.Now().After(until) {
- down = true
- }
-
- fails := atomic.LoadInt32(&uh.Fails)
- if fails >= upstream.MaxFails && upstream.MaxFails != 0 {
- down = true
- }
- return down
- }
- }(upstream),
- WithoutPathPrefix: upstream.WithoutPathPrefix,
- }
-
- upstream.Hosts[i] = uh
- }
- upstream.Start()
-
- upstreams = append(upstreams, upstream)
- }
- return upstreams, nil
-}
-
-func (u *staticUpstream) From() string {
- return u.from
-}
-
-func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error {
- switch c.Val() {
- case "policy":
- if !c.NextArg() {
- return c.ArgErr()
- }
- policyCreateFunc, ok := healthcheck.SupportedPolicies[c.Val()]
- if !ok {
- return c.ArgErr()
- }
- u.Policy = policyCreateFunc()
- case "fail_timeout":
- if !c.NextArg() {
- return c.ArgErr()
- }
- dur, err := time.ParseDuration(c.Val())
- if err != nil {
- return err
- }
- u.FailTimeout = dur
- case "max_fails":
- if !c.NextArg() {
- return c.ArgErr()
- }
- n, err := strconv.Atoi(c.Val())
- if err != nil {
- return err
- }
- u.MaxFails = int32(n)
- case "health_check":
- if !c.NextArg() {
- return c.ArgErr()
- }
- var err error
- u.HealthCheck.Path, u.HealthCheck.Port, err = net.SplitHostPort(c.Val())
- if err != nil {
- return err
- }
- u.HealthCheck.Interval = 30 * time.Second
- if c.NextArg() {
- dur, err := time.ParseDuration(c.Val())
- if err != nil {
- return err
- }
- u.HealthCheck.Interval = dur
- u.Future = 2 * dur
-
- // set a minimum of 3 seconds
- if u.Future < (3 * time.Second) {
- u.Future = 3 * time.Second
- }
- }
- case "without":
- if !c.NextArg() {
- return c.ArgErr()
- }
- u.WithoutPathPrefix = c.Val()
- case "except":
- ignoredDomains := c.RemainingArgs()
- if len(ignoredDomains) == 0 {
- return c.ArgErr()
- }
- for i := 0; i < len(ignoredDomains); i++ {
- ignoredDomains[i] = middleware.Host(ignoredDomains[i]).Normalize()
- }
- u.IgnoredSubDomains = ignoredDomains
- case "spray":
- u.Spray = &healthcheck.Spray{}
- case "protocol":
- encArgs := c.RemainingArgs()
- if len(encArgs) == 0 {
- return c.ArgErr()
- }
- switch encArgs[0] {
- case "dns":
- if len(encArgs) > 1 {
- if encArgs[1] == "force_tcp" {
- opts := Options{ForceTCP: true}
- u.ex = newDNSExWithOption(opts)
- } else {
- return fmt.Errorf("only force_tcp allowed as parameter to dns")
- }
- } else {
- u.ex = newDNSEx()
- }
- case "https_google":
- boot := []string{"8.8.8.8:53", "8.8.4.4:53"}
- if len(encArgs) > 2 && encArgs[1] == "bootstrap" {
- boot = encArgs[2:]
- }
-
- u.ex = newGoogle("", boot) // "" for default in google.go
- case "grpc":
- if len(encArgs) == 2 && encArgs[1] == "insecure" {
- u.ex = newGrpcClient(nil, u)
- return nil
- }
- tls, err := tls.NewTLSConfigFromArgs(encArgs[1:]...)
- if err != nil {
- return err
- }
- u.ex = newGrpcClient(tls, u)
- default:
- return fmt.Errorf("%s: %s", errInvalidProtocol, encArgs[0])
- }
-
- default:
- return c.Errf("unknown property '%s'", c.Val())
- }
- return nil
-}
-
-func (u *staticUpstream) IsAllowedDomain(name string) bool {
- if dns.Name(name) == dns.Name(u.From()) {
- return true
- }
-
- for _, ignoredSubDomain := range u.IgnoredSubDomains {
- if middleware.Name(ignoredSubDomain).Matches(name) {
- return false
- }
- }
- return true
-}
-
-func (u *staticUpstream) Exchanger() Exchanger { return u.ex }
diff --git a/middleware/proxy/upstream_test.go b/middleware/proxy/upstream_test.go
deleted file mode 100644
index 3ee225c2d..000000000
--- a/middleware/proxy/upstream_test.go
+++ /dev/null
@@ -1,324 +0,0 @@
-package proxy
-
-import (
- "path/filepath"
- "strings"
- "testing"
-
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/mholt/caddy"
-)
-
-func TestAllowedDomain(t *testing.T) {
- upstream := &staticUpstream{
- from: "miek.nl.",
- IgnoredSubDomains: []string{"download.miek.nl.", "static.miek.nl."}, // closing dot mandatory
- }
- tests := []struct {
- name string
- expected bool
- }{
- {"miek.nl.", true},
- {"download.miek.nl.", false},
- {"static.miek.nl.", false},
- {"blaat.miek.nl.", true},
- }
-
- for i, test := range tests {
- isAllowed := upstream.IsAllowedDomain(test.name)
- if test.expected != isAllowed {
- t.Errorf("Test %d: expected %v found %v for %s", i+1, test.expected, isAllowed, test.name)
- }
- }
-}
-
-func TestProxyParse(t *testing.T) {
- rmFunc, cert, key, ca := getPEMFiles(t)
- defer rmFunc()
-
- grpc1 := "proxy . 8.8.8.8:53 {\n protocol grpc " + ca + "\n}"
- grpc2 := "proxy . 8.8.8.8:53 {\n protocol grpc " + cert + " " + key + "\n}"
- grpc3 := "proxy . 8.8.8.8:53 {\n protocol grpc " + cert + " " + key + " " + ca + "\n}"
- grpc4 := "proxy . 8.8.8.8:53 {\n protocol grpc " + key + "\n}"
-
- tests := []struct {
- inputUpstreams string
- shouldErr bool
- }{
- {
- `proxy . 8.8.8.8:53`,
- false,
- },
- {
- `proxy 10.0.0.0/24 8.8.8.8:53`,
- false,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- policy round_robin
-}`,
- false,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- fail_timeout 5s
-}`,
- false,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- max_fails 10
-}`,
- false,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- health_check /health:8080
-}`,
- false,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- without without
-}`,
- false,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- except miek.nl example.org 10.0.0.0/24
-}`,
- false,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- spray
-}`,
- false,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- error_option
-}`,
- true,
- },
- {
- `
-proxy . some_bogus_filename`,
- true,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- protocol dns
-}`,
- false,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- protocol grpc
-}`,
- false,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- protocol grpc insecure
-}`,
- false,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- protocol dns force_tcp
-}`,
- false,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- protocol grpc a b c d
-}`,
- true,
- },
- {
- grpc1,
- false,
- },
- {
- grpc2,
- false,
- },
- {
- grpc3,
- false,
- },
- {
- grpc4,
- true,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- protocol foobar
-}`,
- true,
- },
- {
- `proxy`,
- true,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- protocol foobar
-}`,
- true,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- policy
-}`,
- true,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- fail_timeout
-}`,
- true,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- fail_timeout junky
-}`,
- true,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- health_check
-}`,
- true,
- },
- {
- `
-proxy . 8.8.8.8:53 {
- protocol dns force
-}`,
- true,
- },
- }
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.inputUpstreams)
- _, err := NewStaticUpstreams(&c.Dispenser)
- if (err != nil) != test.shouldErr {
- t.Errorf("Test %d expected no error, got %v for %s", i+1, err, test.inputUpstreams)
- }
- }
-}
-
-func TestResolvParse(t *testing.T) {
- tests := []struct {
- inputUpstreams string
- filedata string
- shouldErr bool
- expected []string
- }{
- {
- `
-proxy . FILE
-`,
- `
-nameserver 1.2.3.4
-nameserver 4.3.2.1
-`,
- false,
- []string{"1.2.3.4:53", "4.3.2.1:53"},
- },
- {
- `
-proxy example.com 1.1.1.1:5000
-proxy . FILE
-proxy example.org 2.2.2.2:1234
-`,
- `
-nameserver 1.2.3.4
-`,
- false,
- []string{"1.1.1.1:5000", "1.2.3.4:53", "2.2.2.2:1234"},
- },
- {
- `
-proxy example.com 1.1.1.1:5000
-proxy . FILE
-proxy example.org 2.2.2.2:1234
-`,
- `
-junky resolve.conf
-`,
- false,
- []string{"1.1.1.1:5000", "2.2.2.2:1234"},
- },
- }
- for i, tc := range tests {
-
- path, rm, err := test.TempFile(".", tc.filedata)
- if err != nil {
- t.Fatalf("Test %d could not creat temp file %v", i, err)
- }
- defer rm()
-
- config := strings.Replace(tc.inputUpstreams, "FILE", path, -1)
- c := caddy.NewTestController("dns", config)
- upstreams, err := NewStaticUpstreams(&c.Dispenser)
- if (err != nil) != tc.shouldErr {
- t.Errorf("Test %d expected no error, got %v", i+1, err)
- }
- var hosts []string
- for _, u := range upstreams {
- for _, h := range u.(*staticUpstream).Hosts {
- hosts = append(hosts, h.Name)
- }
- }
- if !tc.shouldErr {
- if len(hosts) != len(tc.expected) {
- t.Errorf("Test %d expected %d hosts got %d", i+1, len(tc.expected), len(upstreams))
- } else {
- ok := true
- for i, v := range tc.expected {
- if v != hosts[i] {
- ok = false
- }
- }
- if !ok {
- t.Errorf("Test %d expected %v got %v", i+1, tc.expected, upstreams)
- }
- }
- }
- }
-}
-
-func getPEMFiles(t *testing.T) (rmFunc func(), cert, key, ca string) {
- tempDir, rmFunc, err := test.WritePEMFiles("")
- if err != nil {
- t.Fatalf("Could not write PEM files: %s", err)
- }
-
- cert = filepath.Join(tempDir, "cert.pem")
- key = filepath.Join(tempDir, "key.pem")
- ca = filepath.Join(tempDir, "ca.pem")
-
- return
-}
diff --git a/middleware/reverse/README.md b/middleware/reverse/README.md
deleted file mode 100644
index 2bc4b2e1d..000000000
--- a/middleware/reverse/README.md
+++ /dev/null
@@ -1,86 +0,0 @@
-# reverse
-
-The *reverse* middleware allows CoreDNS to respond dynamically to a PTR request and the related A/AAAA request.
-
-## Syntax
-
-~~~
-reverse NETWORK... {
- hostname TEMPLATE
- [ttl TTL]
- [fallthrough]
- [wildcard]
-~~~
-
-* **NETWORK** one or more CIDR formatted networks to respond on.
-* `hostname` injects the IP and zone to a template for the hostname. Defaults to "ip-{IP}.{zone[1]}". See below for template.
-* `ttl` defaults to 60
-* `fallthrough` if zone matches and no record can be generated, pass request to the next middleware.
-* `wildcard` allows matches to catch all subdomains as well.
-
-### Template Syntax
-
-The template for the hostname is used for generating the PTR for a reverse lookup and matching the
-forward lookup back to an IP.
-
-#### `{ip}`
-
-The `{ip}` symbol is **required** to make reverse work.
-For IPv4 lookups the IP is directly extracted
-With IPv6 lookups the ":" is removed, and any zero ranged are expanded, e.g.,
-"ffff::ffff" results in "ffff000000000000000000000000ffff"
-
-#### `{zone[i]}`
-
-The `{zone[i]}` symbol is **optional** and can be replaced by a fixed (zone) string.
-The zone will be matched by the zones listed in *this* configuration stanza.
-`i` needs to be replaced with the index of the configured listener zones, starting with 1.
-
-## Examples
-
-~~~ txt
-arpa compute.internal {
- # proxy unmatched requests
- proxy . 8.8.8.8
-
- # answer requests for IPs in this network
- # PTR 1.0.32.10.in-addr.arpa. 3600 ip-10.0.32.1.compute.internal.
- # A ip-10.0.32.1.compute.internal. 3600 10.0.32.1
- # v6 is also possible
- # PTR 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.d.f.ip6.arpa. 3600 ip-fd010000000000000000000000000001.compute.internal.
- # AAAA ip-fd010000000000000000000000000001.compute.internal. 3600 fd01::1
- reverse 10.32.0.0/16 fd01::/16 {
- # template of the ip injection to hostname, zone resolved to compute.internal.
- hostname ip-{ip}.{zone[2]}
-
- ttl 3600
-
- # Forward unanswered or unmatched requests to proxy
- # without this flag, requesting A/AAAA records on compute.internal. will end here.
- fallthrough
- }
-}
-~~~
-
-
-~~~ txt
-32.10.in-addr.arpa.arpa arpa.company.org {
-
- reverse 10.32.0.0/16 {
- # template of the ip injection to hostname, zone resolved to arpa.company.org.
- hostname "ip-{ip}.v4.{zone[2]}"
-
- ttl 3600
-
- # fallthrough is not required, v4.arpa.company.org. will be only answered here
- }
-
- # cidr closer to the ip wins, so we can overwrite the "default"
- reverse 10.32.2.0/24 {
- # its also possible to set fix domain suffix
- hostname ip-{ip}.fix.arpa.company.org.
-
- ttl 3600
- }
-}
-~~~
diff --git a/middleware/reverse/network.go b/middleware/reverse/network.go
deleted file mode 100644
index 80d533382..000000000
--- a/middleware/reverse/network.go
+++ /dev/null
@@ -1,87 +0,0 @@
-package reverse
-
-import (
- "bytes"
- "net"
- "regexp"
- "strings"
-)
-
-type network struct {
- IPnet *net.IPNet
- Zone string // forward lookup zone
- Template string
- TTL uint32
- RegexMatchIP *regexp.Regexp
-}
-
-// TODO: we might want to get rid of these regexes.
-const hexDigit = "0123456789abcdef"
-const templateNameIP = "{ip}"
-const regexMatchV4 = "((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))"
-const regexMatchV6 = "([0-9a-fA-F]{32})"
-
-// hostnameToIP converts the hostname back to an ip, based on the template
-// returns nil if there is no IP found.
-func (network *network) hostnameToIP(rname string) net.IP {
- var matchedIP net.IP
-
- match := network.RegexMatchIP.FindStringSubmatch(rname)
- if len(match) != 2 {
- return nil
- }
-
- if network.IPnet.IP.To4() != nil {
- matchedIP = net.ParseIP(match[1])
- } else {
- // TODO: can probably just allocate a []byte and use that.
- var buf bytes.Buffer
- // convert back to an valid ipv6 string with colons
- for i := 0; i < 8*4; i += 4 {
- buf.WriteString(match[1][i : i+4])
- if i < 28 {
- buf.WriteString(":")
- }
- }
- matchedIP = net.ParseIP(buf.String())
- }
-
- // No valid ip or it does not belong to this network
- if matchedIP == nil || !network.IPnet.Contains(matchedIP) {
- return nil
- }
-
- return matchedIP
-}
-
-// ipToHostname converts an IP to an DNS compatible hostname and injects it into the template.domain.
-func (network *network) ipToHostname(ip net.IP) (name string) {
- if ipv4 := ip.To4(); ipv4 != nil {
- // replace . to -
- name = ipv4.String()
- } else {
- // assume v6
- // ensure zeros are present in string
- buf := make([]byte, 0, len(ip)*4)
- for i := 0; i < len(ip); i++ {
- v := ip[i]
- buf = append(buf, hexDigit[v>>4])
- buf = append(buf, hexDigit[v&0xF])
- }
- name = string(buf)
- }
- // inject the converted ip into the fqdn template
- return strings.Replace(network.Template, templateNameIP, name, 1)
-}
-
-type networks []network
-
-func (n networks) Len() int { return len(n) }
-func (n networks) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
-
-// cidr closer to the ip wins (by netmask)
-func (n networks) Less(i, j int) bool {
- isize, _ := n[i].IPnet.Mask.Size()
- jsize, _ := n[j].IPnet.Mask.Size()
- return isize > jsize
-}
diff --git a/middleware/reverse/network_test.go b/middleware/reverse/network_test.go
deleted file mode 100644
index a826707e5..000000000
--- a/middleware/reverse/network_test.go
+++ /dev/null
@@ -1,135 +0,0 @@
-package reverse
-
-import (
- "net"
- "reflect"
- "regexp"
- "testing"
-)
-
-// Test converting from hostname to IP and back again to hostname
-func TestNetworkConversion(t *testing.T) {
-
- _, net4, _ := net.ParseCIDR("10.1.1.0/24")
- _, net6, _ := net.ParseCIDR("fd01::/64")
-
- regexIP4, _ := regexp.Compile("^dns-" + regexMatchV4 + "\\.domain\\.internal\\.$")
- regexIP6, _ := regexp.Compile("^dns-" + regexMatchV6 + "\\.domain\\.internal\\.$")
-
- tests := []struct {
- network network
- resultHost string
- resultIP net.IP
- }{
- {
- network{
- IPnet: net4,
- Template: "dns-{ip}.domain.internal.",
- RegexMatchIP: regexIP4,
- },
- "dns-10.1.1.23.domain.internal.",
- net.ParseIP("10.1.1.23"),
- },
- {
- network{
- IPnet: net6,
- Template: "dns-{ip}.domain.internal.",
- RegexMatchIP: regexIP6,
- },
- "dns-fd01000000000000000000000000a32f.domain.internal.",
- net.ParseIP("fd01::a32f"),
- },
- }
-
- for i, test := range tests {
- resultIP := test.network.hostnameToIP(test.resultHost)
- if !reflect.DeepEqual(test.resultIP, resultIP) {
- t.Fatalf("Test %d expected %v, got %v", i, test.resultIP, resultIP)
- }
-
- resultHost := test.network.ipToHostname(test.resultIP)
- if !reflect.DeepEqual(test.resultHost, resultHost) {
- t.Fatalf("Test %d expected %v, got %v", i, test.resultHost, resultHost)
- }
- }
-}
-
-func TestNetworkHostnameToIP(t *testing.T) {
-
- _, net4, _ := net.ParseCIDR("10.1.1.0/24")
- _, net6, _ := net.ParseCIDR("fd01::/64")
-
- regexIP4, _ := regexp.Compile("^dns-" + regexMatchV4 + "\\.domain\\.internal\\.$")
- regexIP6, _ := regexp.Compile("^dns-" + regexMatchV6 + "\\.domain\\.internal\\.$")
-
- // Test regex does NOT match
- // All this test should return nil
- testsNil := []struct {
- network network
- hostname string
- }{
- {
- network{
- IPnet: net4,
- RegexMatchIP: regexIP4,
- },
- // domain does not match
- "dns-10.1.1.23.domain.internals.",
- },
- {
- network{
- IPnet: net4,
- RegexMatchIP: regexIP4,
- },
- // IP does match / contain in subnet
- "dns-200.1.1.23.domain.internals.",
- },
- {
- network{
- IPnet: net4,
- RegexMatchIP: regexIP4,
- },
- // template does not match
- "dns-10.1.1.23-x.domain.internal.",
- },
- {
- network{
- IPnet: net4,
- RegexMatchIP: regexIP4,
- },
- // template does not match
- "IP-dns-10.1.1.23.domain.internal.",
- },
- {
- network{
- IPnet: net6,
- RegexMatchIP: regexIP6,
- },
- // template does not match
- "dnx-fd01000000000000000000000000a32f.domain.internal.",
- },
- {
- network{
- IPnet: net6,
- RegexMatchIP: regexIP6,
- },
- // no valid v6 (missing one 0, only 31 chars)
- "dns-fd0100000000000000000000000a32f.domain.internal.",
- },
- {
- network{
- IPnet: net6,
- RegexMatchIP: regexIP6,
- },
- // IP does match / contain in subnet
- "dns-ed01000000000000000000000000a32f.domain.internal.",
- },
- }
-
- for i, test := range testsNil {
- resultIP := test.network.hostnameToIP(test.hostname)
- if resultIP != nil {
- t.Fatalf("Test %d expected nil, got %v", i, resultIP)
- }
- }
-}
diff --git a/middleware/reverse/reverse.go b/middleware/reverse/reverse.go
deleted file mode 100644
index eb14ae155..000000000
--- a/middleware/reverse/reverse.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package reverse
-
-import (
- "net"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// Reverse provides dynamic reverse DNS and the related forward RR.
-type Reverse struct {
- Next middleware.Handler
- Networks networks
- Fallthrough bool
-}
-
-// ServeDNS implements the middleware.Handler interface.
-func (re Reverse) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- var rr dns.RR
-
- state := request.Request{W: w, Req: r}
- m := new(dns.Msg)
- m.SetReply(r)
- m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
-
- switch state.QType() {
- case dns.TypePTR:
- address := dnsutil.ExtractAddressFromReverse(state.Name())
-
- if address == "" {
- // Not an reverse lookup, but can still be an pointer for an domain
- break
- }
-
- ip := net.ParseIP(address)
- // loop through the configured networks
- for _, n := range re.Networks {
- if n.IPnet.Contains(ip) {
- rr = &dns.PTR{
- Hdr: dns.RR_Header{Name: state.QName(), Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: n.TTL},
- Ptr: n.ipToHostname(ip),
- }
- break
- }
- }
-
- case dns.TypeA:
- for _, n := range re.Networks {
- if dns.IsSubDomain(n.Zone, state.Name()) {
-
- // skip if requesting an v4 address and network is not v4
- if n.IPnet.IP.To4() == nil {
- continue
- }
-
- result := n.hostnameToIP(state.Name())
- if result != nil {
- rr = &dns.A{
- Hdr: dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: n.TTL},
- A: result,
- }
- break
- }
- }
- }
-
- case dns.TypeAAAA:
- for _, n := range re.Networks {
- if dns.IsSubDomain(n.Zone, state.Name()) {
-
- // Do not use To16 which tries to make v4 in v6
- if n.IPnet.IP.To4() != nil {
- continue
- }
-
- result := n.hostnameToIP(state.Name())
- if result != nil {
- rr = &dns.AAAA{
- Hdr: dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: n.TTL},
- AAAA: result,
- }
- break
- }
- }
- }
-
- }
-
- if rr != nil {
- m.Answer = append(m.Answer, rr)
- state.SizeAndDo(m)
- w.WriteMsg(m)
- return dns.RcodeSuccess, nil
- }
-
- if re.Fallthrough {
- return middleware.NextOrFailure(re.Name(), re.Next, ctx, w, r)
- }
- return dns.RcodeServerFailure, nil
-}
-
-// Name implements the Handler interface.
-func (re Reverse) Name() string { return "reverse" }
diff --git a/middleware/reverse/reverse_test.go b/middleware/reverse/reverse_test.go
deleted file mode 100644
index 4b17f0971..000000000
--- a/middleware/reverse/reverse_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package reverse
-
-import (
- "net"
- "regexp"
- "testing"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func TestReverse(t *testing.T) {
- _, net4, _ := net.ParseCIDR("10.1.1.0/24")
- regexIP4, _ := regexp.Compile("^.*ip-" + regexMatchV4 + "\\.example\\.org\\.$")
-
- em := Reverse{
- Networks: networks{network{
- IPnet: net4,
- Zone: "example.org",
- Template: "ip-{ip}.example.org.",
- RegexMatchIP: regexIP4,
- }},
- Fallthrough: false,
- }
-
- tests := []struct {
- next middleware.Handler
- qname string
- qtype uint16
- expectedCode int
- expectedReply string
- expectedErr error
- }{
- {
- next: test.NextHandler(dns.RcodeSuccess, nil),
- qname: "test.ip-10.1.1.2.example.org",
- expectedCode: dns.RcodeSuccess,
- expectedReply: "10.1.1.2",
- expectedErr: nil,
- },
- }
-
- ctx := context.TODO()
-
- for i, tr := range tests {
- req := new(dns.Msg)
-
- tr.qtype = dns.TypeA
- req.SetQuestion(dns.Fqdn(tr.qname), tr.qtype)
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- code, err := em.ServeDNS(ctx, rec, req)
-
- if err != tr.expectedErr {
- t.Errorf("Test %d: Expected error %v, but got %v", i, tr.expectedErr, err)
- }
- if code != int(tr.expectedCode) {
- t.Errorf("Test %d: Expected status code %d, but got %d", i, tr.expectedCode, code)
- }
- if tr.expectedReply != "" {
- answer := rec.Msg.Answer[0].(*dns.A).A.String()
- if answer != tr.expectedReply {
- t.Errorf("Test %d: Expected answer %s, but got %s", i, tr.expectedReply, answer)
- }
- }
- }
-}
diff --git a/middleware/reverse/setup.go b/middleware/reverse/setup.go
deleted file mode 100644
index 8d8cc6548..000000000
--- a/middleware/reverse/setup.go
+++ /dev/null
@@ -1,147 +0,0 @@
-package reverse
-
-import (
- "net"
- "regexp"
- "sort"
- "strconv"
- "strings"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("reverse", caddy.Plugin{
- ServerType: "dns",
- Action: setupReverse,
- })
-}
-
-func setupReverse(c *caddy.Controller) error {
- networks, fallThrough, err := reverseParse(c)
- if err != nil {
- return middleware.Error("reverse", err)
- }
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- return Reverse{Next: next, Networks: networks, Fallthrough: fallThrough}
- })
-
- return nil
-}
-
-func reverseParse(c *caddy.Controller) (nets networks, fall bool, err error) {
- zones := make([]string, len(c.ServerBlockKeys))
- wildcard := false
-
- // We copy from the serverblock, these contains Hosts.
- for i, str := range c.ServerBlockKeys {
- zones[i] = middleware.Host(str).Normalize()
- }
-
- for c.Next() {
- var cidrs []*net.IPNet
-
- // parse all networks
- for _, cidr := range c.RemainingArgs() {
- if cidr == "{" {
- break
- }
- _, ipnet, err := net.ParseCIDR(cidr)
- if err != nil {
- return nil, false, c.Errf("network needs to be CIDR formatted: %q\n", cidr)
- }
- cidrs = append(cidrs, ipnet)
- }
- if len(cidrs) == 0 {
- return nil, false, c.ArgErr()
- }
-
- // set defaults
- var (
- template = "ip-" + templateNameIP + ".{zone[1]}"
- ttl = 60
- )
- for c.NextBlock() {
- switch c.Val() {
- case "hostname":
- if !c.NextArg() {
- return nil, false, c.ArgErr()
- }
- template = c.Val()
-
- case "ttl":
- if !c.NextArg() {
- return nil, false, c.ArgErr()
- }
- ttl, err = strconv.Atoi(c.Val())
- if err != nil {
- return nil, false, err
- }
-
- case "wildcard":
- wildcard = true
-
- case "fallthrough":
- fall = true
-
- default:
- return nil, false, c.ArgErr()
- }
- }
-
- // prepare template
- // replace {zone[index]} by the listen zone/domain of this config block
- for i, zone := range zones {
- // TODO: we should be smarter about actually replacing this. This works, but silently allows "zone[-1]"
- // for instance.
- template = strings.Replace(template, "{zone["+strconv.Itoa(i+1)+"]}", zone, 1)
- }
- if !strings.HasSuffix(template, ".") {
- template += "."
- }
-
- // extract zone from template
- templateZone := strings.SplitAfterN(template, ".", 2)
- if len(templateZone) != 2 || templateZone[1] == "" {
- return nil, false, c.Errf("cannot find domain in template '%v'", template)
- }
-
- // Create for each configured network in this stanza
- for _, ipnet := range cidrs {
- // precompile regex for hostname to ip matching
- regexIP := regexMatchV4
- if ipnet.IP.To4() == nil {
- regexIP = regexMatchV6
- }
- prefix := "^"
- if wildcard {
- prefix += ".*"
- }
- regex, err := regexp.Compile(
- prefix + strings.Replace( // inject ip regex into template
- regexp.QuoteMeta(template), // escape dots
- regexp.QuoteMeta(templateNameIP),
- regexIP,
- 1) + "$")
- if err != nil {
- return nil, false, err
- }
-
- nets = append(nets, network{
- IPnet: ipnet,
- Zone: templateZone[1],
- Template: template,
- RegexMatchIP: regex,
- TTL: uint32(ttl),
- })
- }
- }
-
- // sort by cidr
- sort.Sort(nets)
- return nets, fall, nil
-}
diff --git a/middleware/reverse/setup_test.go b/middleware/reverse/setup_test.go
deleted file mode 100644
index 5b4c04e82..000000000
--- a/middleware/reverse/setup_test.go
+++ /dev/null
@@ -1,195 +0,0 @@
-package reverse
-
-import (
- "net"
- "reflect"
- "regexp"
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestSetupParse(t *testing.T) {
-
- _, net4, _ := net.ParseCIDR("10.1.1.0/24")
- _, net6, _ := net.ParseCIDR("fd01::/64")
-
- regexIP4wildcard, _ := regexp.Compile("^.*ip-" + regexMatchV4 + "\\.domain\\.com\\.$")
- regexIP6, _ := regexp.Compile("^ip-" + regexMatchV6 + "\\.domain\\.com\\.$")
- regexIpv4dynamic, _ := regexp.Compile("^dynamic-" + regexMatchV4 + "-intern\\.dynamic\\.domain\\.com\\.$")
- regexIpv6dynamic, _ := regexp.Compile("^dynamic-" + regexMatchV6 + "-intern\\.dynamic\\.domain\\.com\\.$")
- regexIpv4vpndynamic, _ := regexp.Compile("^dynamic-" + regexMatchV4 + "-vpn\\.dynamic\\.domain\\.com\\.$")
-
- serverBlockKeys := []string{"domain.com.:8053", "dynamic.domain.com.:8053"}
-
- tests := []struct {
- inputFileRules string
- shouldErr bool
- networks networks
- }{
- {
- // with defaults
- `reverse fd01::/64`,
- false,
- networks{network{
- IPnet: net6,
- Template: "ip-{ip}.domain.com.",
- Zone: "domain.com.",
- TTL: 60,
- RegexMatchIP: regexIP6,
- }},
- },
- {
- `reverse`,
- true,
- networks{},
- },
- {
- //no cidr
- `reverse 10.1.1.1`,
- true,
- networks{},
- },
- {
- //no cidr
- `reverse 10.1.1.0/16 fd00::`,
- true,
- networks{},
- },
- {
- // invalid key
- `reverse 10.1.1.0/24 {
- notavailable
- }`,
- true,
- networks{},
- },
- {
- // no domain suffix
- `reverse 10.1.1.0/24 {
- hostname ip-{ip}.
- }`,
- true,
- networks{},
- },
- {
- // hostname requires an second arg
- `reverse 10.1.1.0/24 {
- hostname
- }`,
- true,
- networks{},
- },
- {
- // template breaks regex compile
- `reverse 10.1.1.0/24 {
- hostname ip-{[-x
- }`,
- true,
- networks{},
- },
- {
- // ttl requires an (u)int
- `reverse 10.1.1.0/24 {
- ttl string
- }`,
- true,
- networks{},
- },
- {
- `reverse fd01::/64 {
- hostname dynamic-{ip}-intern.{zone[2]}
- ttl 50
- }
- reverse 10.1.1.0/24 {
- hostname dynamic-{ip}-vpn.{zone[2]}
- fallthrough
- }`,
- false,
- networks{network{
- IPnet: net6,
- Template: "dynamic-{ip}-intern.dynamic.domain.com.",
- Zone: "dynamic.domain.com.",
- TTL: 50,
- RegexMatchIP: regexIpv6dynamic,
- }, network{
- IPnet: net4,
- Template: "dynamic-{ip}-vpn.dynamic.domain.com.",
- Zone: "dynamic.domain.com.",
- TTL: 60,
- RegexMatchIP: regexIpv4vpndynamic,
- }},
- },
- {
- // multiple networks in one stanza
- `reverse fd01::/64 10.1.1.0/24 {
- hostname dynamic-{ip}-intern.{zone[2]}
- ttl 50
- fallthrough
- }`,
- false,
- networks{network{
- IPnet: net6,
- Template: "dynamic-{ip}-intern.dynamic.domain.com.",
- Zone: "dynamic.domain.com.",
- TTL: 50,
- RegexMatchIP: regexIpv6dynamic,
- }, network{
- IPnet: net4,
- Template: "dynamic-{ip}-intern.dynamic.domain.com.",
- Zone: "dynamic.domain.com.",
- TTL: 50,
- RegexMatchIP: regexIpv4dynamic,
- }},
- },
- {
- // fix domain in template
- `reverse fd01::/64 {
- hostname dynamic-{ip}-intern.dynamic.domain.com
- ttl 300
- fallthrough
- }`,
- false,
- networks{network{
- IPnet: net6,
- Template: "dynamic-{ip}-intern.dynamic.domain.com.",
- Zone: "dynamic.domain.com.",
- TTL: 300,
- RegexMatchIP: regexIpv6dynamic,
- }},
- },
- {
- `reverse 10.1.1.0/24 {
- hostname ip-{ip}.{zone[1]}
- ttl 50
- wildcard
- fallthrough
- }`,
- false,
- networks{network{
- IPnet: net4,
- Template: "ip-{ip}.domain.com.",
- Zone: "domain.com.",
- TTL: 50,
- RegexMatchIP: regexIP4wildcard,
- }},
- },
- }
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.inputFileRules)
- c.ServerBlockKeys = serverBlockKeys
- networks, _, err := reverseParse(c)
-
- if err == nil && test.shouldErr {
- t.Fatalf("Test %d expected errors, but got no error", i)
- } else if err != nil && !test.shouldErr {
- t.Fatalf("Test %d expected no errors, but got '%v'", i, err)
- }
- for j, n := range networks {
- reflect.DeepEqual(test.networks[j], n)
- if !reflect.DeepEqual(test.networks[j], n) {
- t.Fatalf("Test %d/%d expected %v, got %v", i, j, test.networks[j], n)
- }
- }
- }
-}
diff --git a/middleware/rewrite/README.md b/middleware/rewrite/README.md
deleted file mode 100644
index 63334d09c..000000000
--- a/middleware/rewrite/README.md
+++ /dev/null
@@ -1,91 +0,0 @@
-# rewrite
-
-*rewrite* performs internal message rewriting.
-
-Rewrites are invisible to the client. There are simple rewrites (fast) and complex rewrites
-(slower), but they're powerful enough to accommodate most dynamic back-end applications.
-
-## Syntax
-
-~~~
-rewrite FIELD FROM TO
-~~~
-
-* **FIELD** is (`type`, `class`, `name`, ...)
-* **FROM** is the exact name of type to match
-* **TO** is the destination name or type to rewrite to
-
-When the FIELD is `type` and FROM is (`A`, `MX`, etc.), the type of the message will be rewritten;
-e.g., to rewrite ANY queries to HINFO, use `rewrite type ANY HINFO`.
-
-When the FIELD is `class` and FROM is (`IN`, `CH`, or `HS`) the class of the message will be
-rewritten; e.g., to rewrite CH queries to IN use `rewrite class CH IN`.
-
-When the FIELD is `name` the query name in the message is rewritten; this
-needs to be a full match of the name, e.g., `rewrite name miek.nl example.org`.
-
-When the FIELD is `edns0` an EDNS0 option can be appended to the request as described below.
-
-If you specify multiple rules and an incoming query matches on multiple (simple) rules, only
-the first rewrite is applied.
-
-## EDNS0 Options
-
-Using FIELD edns0, you can set, append, or replace specific EDNS0 options on the request.
-
-* `replace` will modify any matching (what that means may vary based on EDNS0 type) option with the specified option
-* `append` will add the option regardless of what options already exist
-* `set` will modify a matching option or add one if none is found
-
-Currently supported are `EDNS0_LOCAL`, `EDNS0_NSID` and `EDNS0_SUBNET`.
-
-### `EDNS0_LOCAL`
-
-This has two fields, code and data. A match is defined as having the same code. Data may be a string or a variable.
-
-* A string data can be treated as hex if it starts with `0x`. Example:
-
-~~~
-rewrite edns0 local set 0xffee 0x61626364
-~~~
-
-rewrites the first local option with code 0xffee, setting the data to "abcd". Equivalent:
-
-~~~
-rewrite edns0 local set 0xffee abcd
-~~~
-
-* A variable data is specified with a pair of curly brackets `{}`. Following are the supported variables:
- * {qname}
- * {qtype}
- * {client_ip}
- * {client_port}
- * {protocol}
- * {server_ip}
- * {server_port}
-
-Example:
-
-~~~
-rewrite edns0 local set 0xffee {client_ip}
-~~~
-
-### `EDNS0_NSID`
-
-This has no fields; it will add an NSID option with an empty string for the NSID. If the option already exists
-and the action is `replace` or `set`, then the NSID in the option will be set to the empty string.
-
-### `EDNS0_SUBNET`
-
-This has two fields, IPv4 bitmask length and IPv6 bitmask length. The bitmask
-length is used to extract the client subnet from the source IP address in the query.
-
-Example:
-
-~~~
- rewrite edns0 subnet set 24 56
-~~~
-
-* If the query has source IP as IPv4, the first 24 bits in the IP will be the network subnet.
-* If the query has source IP as IPv6, the first 56 bits in the IP will be the network subnet.
-
diff --git a/middleware/rewrite/class.go b/middleware/rewrite/class.go
deleted file mode 100644
index 8cc7d26b7..000000000
--- a/middleware/rewrite/class.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package rewrite
-
-import (
- "fmt"
- "strings"
-
- "github.com/miekg/dns"
-)
-
-type classRule struct {
- fromClass, toClass uint16
-}
-
-func newClassRule(fromS, toS string) (Rule, error) {
- var from, to uint16
- var ok bool
- if from, ok = dns.StringToClass[strings.ToUpper(fromS)]; !ok {
- return nil, fmt.Errorf("invalid class %q", strings.ToUpper(fromS))
- }
- if to, ok = dns.StringToClass[strings.ToUpper(toS)]; !ok {
- return nil, fmt.Errorf("invalid class %q", strings.ToUpper(toS))
- }
- return &classRule{fromClass: from, toClass: to}, nil
-}
-
-// Rewrite rewrites the the current request.
-func (rule *classRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result {
- if rule.fromClass > 0 && rule.toClass > 0 {
- if r.Question[0].Qclass == rule.fromClass {
- r.Question[0].Qclass = rule.toClass
- return RewriteDone
- }
- }
- return RewriteIgnored
-}
diff --git a/middleware/rewrite/condition.go b/middleware/rewrite/condition.go
deleted file mode 100644
index d4a54d44a..000000000
--- a/middleware/rewrite/condition.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package rewrite
-
-import (
- "fmt"
- "regexp"
- "strings"
-
- "github.com/coredns/coredns/middleware/pkg/replacer"
-
- "github.com/miekg/dns"
-)
-
-// Operators
-const (
- Is = "is"
- Not = "not"
- Has = "has"
- NotHas = "not_has"
- StartsWith = "starts_with"
- EndsWith = "ends_with"
- Match = "match"
- NotMatch = "not_match"
-)
-
-func operatorError(operator string) error {
- return fmt.Errorf("invalid operator %v", operator)
-}
-
-func newReplacer(r *dns.Msg) replacer.Replacer {
- return replacer.New(r, nil, "")
-}
-
-// condition is a rewrite condition.
-type condition func(string, string) bool
-
-var conditions = map[string]condition{
- Is: isFunc,
- Not: notFunc,
- Has: hasFunc,
- NotHas: notHasFunc,
- StartsWith: startsWithFunc,
- EndsWith: endsWithFunc,
- Match: matchFunc,
- NotMatch: notMatchFunc,
-}
-
-// isFunc is condition for Is operator.
-// It checks for equality.
-func isFunc(a, b string) bool {
- return a == b
-}
-
-// notFunc is condition for Not operator.
-// It checks for inequality.
-func notFunc(a, b string) bool {
- return a != b
-}
-
-// hasFunc is condition for Has operator.
-// It checks if b is a substring of a.
-func hasFunc(a, b string) bool {
- return strings.Contains(a, b)
-}
-
-// notHasFunc is condition for NotHas operator.
-// It checks if b is not a substring of a.
-func notHasFunc(a, b string) bool {
- return !strings.Contains(a, b)
-}
-
-// startsWithFunc is condition for StartsWith operator.
-// It checks if b is a prefix of a.
-func startsWithFunc(a, b string) bool {
- return strings.HasPrefix(a, b)
-}
-
-// endsWithFunc is condition for EndsWith operator.
-// It checks if b is a suffix of a.
-func endsWithFunc(a, b string) bool {
- // TODO(miek): IsSubDomain
- return strings.HasSuffix(a, b)
-}
-
-// matchFunc is condition for Match operator.
-// It does regexp matching of a against pattern in b
-// and returns if they match.
-func matchFunc(a, b string) bool {
- matched, _ := regexp.MatchString(b, a)
- return matched
-}
-
-// notMatchFunc is condition for NotMatch operator.
-// It does regexp matching of a against pattern in b
-// and returns if they do not match.
-func notMatchFunc(a, b string) bool {
- matched, _ := regexp.MatchString(b, a)
- return !matched
-}
-
-// If is statement for a rewrite condition.
-type If struct {
- A string
- Operator string
- B string
-}
-
-// True returns true if the condition is true and false otherwise.
-// If r is not nil, it replaces placeholders before comparison.
-func (i If) True(r *dns.Msg) bool {
- if c, ok := conditions[i.Operator]; ok {
- a, b := i.A, i.B
- if r != nil {
- replacer := newReplacer(r)
- a = replacer.Replace(i.A)
- b = replacer.Replace(i.B)
- }
- return c(a, b)
- }
- return false
-}
-
-// NewIf creates a new If condition.
-func NewIf(a, operator, b string) (If, error) {
- if _, ok := conditions[operator]; !ok {
- return If{}, operatorError(operator)
- }
- return If{
- A: a,
- Operator: operator,
- B: b,
- }, nil
-}
diff --git a/middleware/rewrite/condition_test.go b/middleware/rewrite/condition_test.go
deleted file mode 100644
index 91004f9d7..000000000
--- a/middleware/rewrite/condition_test.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package rewrite
-
-/*
-func TestConditions(t *testing.T) {
- tests := []struct {
- condition string
- isTrue bool
- }{
- {"a is b", false},
- {"a is a", true},
- {"a not b", true},
- {"a not a", false},
- {"a has a", true},
- {"a has b", false},
- {"ba has b", true},
- {"bab has b", true},
- {"bab has bb", false},
- {"a not_has a", false},
- {"a not_has b", true},
- {"ba not_has b", false},
- {"bab not_has b", false},
- {"bab not_has bb", true},
- {"bab starts_with bb", false},
- {"bab starts_with ba", true},
- {"bab starts_with bab", true},
- {"bab ends_with bb", false},
- {"bab ends_with bab", true},
- {"bab ends_with ab", true},
- {"a match *", false},
- {"a match a", true},
- {"a match .*", true},
- {"a match a.*", true},
- {"a match b.*", false},
- {"ba match b.*", true},
- {"ba match b[a-z]", true},
- {"b0 match b[a-z]", false},
- {"b0a match b[a-z]", false},
- {"b0a match b[a-z]+", false},
- {"b0a match b[a-z0-9]+", true},
- {"a not_match *", true},
- {"a not_match a", false},
- {"a not_match .*", false},
- {"a not_match a.*", false},
- {"a not_match b.*", true},
- {"ba not_match b.*", false},
- {"ba not_match b[a-z]", false},
- {"b0 not_match b[a-z]", true},
- {"b0a not_match b[a-z]", true},
- {"b0a not_match b[a-z]+", true},
- {"b0a not_match b[a-z0-9]+", false},
- }
-
- for i, test := range tests {
- str := strings.Fields(test.condition)
- ifCond, err := NewIf(str[0], str[1], str[2])
- if err != nil {
- t.Error(err)
- }
- isTrue := ifCond.True(nil)
- if isTrue != test.isTrue {
- t.Errorf("Test %v: expected %v found %v", i, test.isTrue, isTrue)
- }
- }
-
- invalidOperators := []string{"ss", "and", "if"}
- for _, op := range invalidOperators {
- _, err := NewIf("a", op, "b")
- if err == nil {
- t.Errorf("Invalid operator %v used, expected error.", op)
- }
- }
-
- replaceTests := []struct {
- url string
- condition string
- isTrue bool
- }{
- {"/home", "{uri} match /home", true},
- {"/hom", "{uri} match /home", false},
- {"/hom", "{uri} starts_with /home", false},
- {"/hom", "{uri} starts_with /h", true},
- {"/home/.hiddenfile", `{uri} match \/\.(.*)`, true},
- {"/home/.hiddendir/afile", `{uri} match \/\.(.*)`, true},
- }
-
- for i, test := range replaceTests {
- r, err := http.NewRequest("GET", test.url, nil)
- if err != nil {
- t.Error(err)
- }
- str := strings.Fields(test.condition)
- ifCond, err := NewIf(str[0], str[1], str[2])
- if err != nil {
- t.Error(err)
- }
- isTrue := ifCond.True(r)
- if isTrue != test.isTrue {
- t.Errorf("Test %v: expected %v found %v", i, test.isTrue, isTrue)
- }
- }
-}
-*/
diff --git a/middleware/rewrite/edns0.go b/middleware/rewrite/edns0.go
deleted file mode 100644
index bdfcac6fd..000000000
--- a/middleware/rewrite/edns0.go
+++ /dev/null
@@ -1,425 +0,0 @@
-// Package rewrite is middleware for rewriting requests internally to something different.
-package rewrite
-
-import (
- "encoding/binary"
- "encoding/hex"
- "fmt"
- "net"
- "strconv"
- "strings"
-
- "github.com/coredns/coredns/request"
- "github.com/miekg/dns"
-)
-
-// edns0LocalRule is a rewrite rule for EDNS0_LOCAL options
-type edns0LocalRule struct {
- action string
- code uint16
- data []byte
-}
-
-// edns0VariableRule is a rewrite rule for EDNS0_LOCAL options with variable
-type edns0VariableRule struct {
- action string
- code uint16
- variable string
-}
-
-// ends0NsidRule is a rewrite rule for EDNS0_NSID options
-type edns0NsidRule struct {
- action string
-}
-
-// setupEdns0Opt will retrieve the EDNS0 OPT or create it if it does not exist
-func setupEdns0Opt(r *dns.Msg) *dns.OPT {
- o := r.IsEdns0()
- if o == nil {
- r.SetEdns0(4096, true)
- o = r.IsEdns0()
- }
- return o
-}
-
-// Rewrite will alter the request EDNS0 NSID option
-func (rule *edns0NsidRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result {
- result := RewriteIgnored
- o := setupEdns0Opt(r)
- found := false
-Option:
- for _, s := range o.Option {
- switch e := s.(type) {
- case *dns.EDNS0_NSID:
- if rule.action == Replace || rule.action == Set {
- e.Nsid = "" // make sure it is empty for request
- result = RewriteDone
- }
- found = true
- break Option
- }
- }
-
- // add option if not found
- if !found && (rule.action == Append || rule.action == Set) {
- o.SetDo()
- o.Option = append(o.Option, &dns.EDNS0_NSID{Code: dns.EDNS0NSID, Nsid: ""})
- result = RewriteDone
- }
-
- return result
-}
-
-// Rewrite will alter the request EDNS0 local options
-func (rule *edns0LocalRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result {
- result := RewriteIgnored
- o := setupEdns0Opt(r)
- found := false
- for _, s := range o.Option {
- switch e := s.(type) {
- case *dns.EDNS0_LOCAL:
- if rule.code == e.Code {
- if rule.action == Replace || rule.action == Set {
- e.Data = rule.data
- result = RewriteDone
- }
- found = true
- break
- }
- }
- }
-
- // add option if not found
- if !found && (rule.action == Append || rule.action == Set) {
- o.SetDo()
- var opt dns.EDNS0_LOCAL
- opt.Code = rule.code
- opt.Data = rule.data
- o.Option = append(o.Option, &opt)
- result = RewriteDone
- }
-
- return result
-}
-
-// newEdns0Rule creates an EDNS0 rule of the appropriate type based on the args
-func newEdns0Rule(args ...string) (Rule, error) {
- if len(args) < 2 {
- return nil, fmt.Errorf("too few arguments for an EDNS0 rule")
- }
-
- ruleType := strings.ToLower(args[0])
- action := strings.ToLower(args[1])
- switch action {
- case Append:
- case Replace:
- case Set:
- default:
- return nil, fmt.Errorf("invalid action: %q", action)
- }
-
- switch ruleType {
- case "local":
- if len(args) != 4 {
- return nil, fmt.Errorf("EDNS0 local rules require exactly three args")
- }
- //Check for variable option
- if strings.HasPrefix(args[3], "{") && strings.HasSuffix(args[3], "}") {
- return newEdns0VariableRule(action, args[2], args[3])
- }
- return newEdns0LocalRule(action, args[2], args[3])
- case "nsid":
- if len(args) != 2 {
- return nil, fmt.Errorf("EDNS0 NSID rules do not accept args")
- }
- return &edns0NsidRule{action: action}, nil
- case "subnet":
- if len(args) != 4 {
- return nil, fmt.Errorf("EDNS0 subnet rules require exactly three args")
- }
- return newEdns0SubnetRule(action, args[2], args[3])
- default:
- return nil, fmt.Errorf("invalid rule type %q", ruleType)
- }
-}
-
-func newEdns0LocalRule(action, code, data string) (*edns0LocalRule, error) {
- c, err := strconv.ParseUint(code, 0, 16)
- if err != nil {
- return nil, err
- }
-
- decoded := []byte(data)
- if strings.HasPrefix(data, "0x") {
- decoded, err = hex.DecodeString(data[2:])
- if err != nil {
- return nil, err
- }
- }
- return &edns0LocalRule{action: action, code: uint16(c), data: decoded}, nil
-}
-
-// newEdns0VariableRule creates an EDNS0 rule that handles variable substitution
-func newEdns0VariableRule(action, code, variable string) (*edns0VariableRule, error) {
- c, err := strconv.ParseUint(code, 0, 16)
- if err != nil {
- return nil, err
- }
- //Validate
- if !isValidVariable(variable) {
- return nil, fmt.Errorf("unsupported variable name %q", variable)
- }
- return &edns0VariableRule{action: action, code: uint16(c), variable: variable}, nil
-}
-
-// ipToWire writes IP address to wire/binary format, 4 or 16 bytes depends on IPV4 or IPV6.
-func (rule *edns0VariableRule) ipToWire(family int, ipAddr string) ([]byte, error) {
-
- switch family {
- case 1:
- return net.ParseIP(ipAddr).To4(), nil
- case 2:
- return net.ParseIP(ipAddr).To16(), nil
- }
- return nil, fmt.Errorf("Invalid IP address family (i.e. version) %d", family)
-}
-
-// uint16ToWire writes unit16 to wire/binary format
-func (rule *edns0VariableRule) uint16ToWire(data uint16) []byte {
- buf := make([]byte, 2)
- binary.BigEndian.PutUint16(buf, uint16(data))
- return buf
-}
-
-// portToWire writes port to wire/binary format, 2 bytes
-func (rule *edns0VariableRule) portToWire(portStr string) ([]byte, error) {
-
- port, err := strconv.ParseUint(portStr, 10, 16)
- if err != nil {
- return nil, err
- }
- return rule.uint16ToWire(uint16(port)), nil
-}
-
-// Family returns the family of the transport, 1 for IPv4 and 2 for IPv6.
-func (rule *edns0VariableRule) family(ip net.Addr) int {
- var a net.IP
- if i, ok := ip.(*net.UDPAddr); ok {
- a = i.IP
- }
- if i, ok := ip.(*net.TCPAddr); ok {
- a = i.IP
- }
- if a.To4() != nil {
- return 1
- }
- return 2
-}
-
-// ruleData returns the data specified by the variable
-func (rule *edns0VariableRule) ruleData(w dns.ResponseWriter, r *dns.Msg) ([]byte, error) {
-
- req := request.Request{W: w, Req: r}
- switch rule.variable {
- case queryName:
- //Query name is written as ascii string
- return []byte(req.QName()), nil
-
- case queryType:
- return rule.uint16ToWire(req.QType()), nil
-
- case clientIP:
- return rule.ipToWire(req.Family(), req.IP())
-
- case clientPort:
- return rule.portToWire(req.Port())
-
- case protocol:
- // Proto is written as ascii string
- return []byte(req.Proto()), nil
-
- case serverIP:
- ip, _, err := net.SplitHostPort(w.LocalAddr().String())
- if err != nil {
- ip = w.RemoteAddr().String()
- }
- return rule.ipToWire(rule.family(w.RemoteAddr()), ip)
-
- case serverPort:
- _, port, err := net.SplitHostPort(w.LocalAddr().String())
- if err != nil {
- port = "0"
- }
- return rule.portToWire(port)
- }
-
- return nil, fmt.Errorf("Unable to extract data for variable %s", rule.variable)
-}
-
-// Rewrite will alter the request EDNS0 local options with specified variables
-func (rule *edns0VariableRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result {
- result := RewriteIgnored
-
- data, err := rule.ruleData(w, r)
- if err != nil || data == nil {
- return result
- }
-
- o := setupEdns0Opt(r)
- found := false
- for _, s := range o.Option {
- switch e := s.(type) {
- case *dns.EDNS0_LOCAL:
- if rule.code == e.Code {
- if rule.action == Replace || rule.action == Set {
- e.Data = data
- result = RewriteDone
- }
- found = true
- break
- }
- }
- }
-
- // add option if not found
- if !found && (rule.action == Append || rule.action == Set) {
- o.SetDo()
- var opt dns.EDNS0_LOCAL
- opt.Code = rule.code
- opt.Data = data
- o.Option = append(o.Option, &opt)
- result = RewriteDone
- }
-
- return result
-}
-
-func isValidVariable(variable string) bool {
- switch variable {
- case
- queryName,
- queryType,
- clientIP,
- clientPort,
- protocol,
- serverIP,
- serverPort:
- return true
- }
- return false
-}
-
-// ends0SubnetRule is a rewrite rule for EDNS0 subnet options
-type edns0SubnetRule struct {
- v4BitMaskLen uint8
- v6BitMaskLen uint8
- action string
-}
-
-func newEdns0SubnetRule(action, v4BitMaskLen, v6BitMaskLen string) (*edns0SubnetRule, error) {
- v4Len, err := strconv.ParseUint(v4BitMaskLen, 0, 16)
- if err != nil {
- return nil, err
- }
- // Validate V4 length
- if v4Len > maxV4BitMaskLen {
- return nil, fmt.Errorf("invalid IPv4 bit mask length %d", v4Len)
- }
-
- v6Len, err := strconv.ParseUint(v6BitMaskLen, 0, 16)
- if err != nil {
- return nil, err
- }
- //Validate V6 length
- if v6Len > maxV6BitMaskLen {
- return nil, fmt.Errorf("invalid IPv6 bit mask length %d", v6Len)
- }
-
- return &edns0SubnetRule{action: action,
- v4BitMaskLen: uint8(v4Len), v6BitMaskLen: uint8(v6Len)}, nil
-}
-
-// fillEcsData sets the subnet data into the ecs option
-func (rule *edns0SubnetRule) fillEcsData(w dns.ResponseWriter, r *dns.Msg,
- ecs *dns.EDNS0_SUBNET) error {
-
- req := request.Request{W: w, Req: r}
- family := req.Family()
- if (family != 1) && (family != 2) {
- return fmt.Errorf("unable to fill data for EDNS0 subnet due to invalid IP family")
- }
-
- ecs.DraftOption = false
- ecs.Family = uint16(family)
- ecs.SourceScope = 0
-
- ipAddr := req.IP()
- switch family {
- case 1:
- ipv4Mask := net.CIDRMask(int(rule.v4BitMaskLen), 32)
- ipv4Addr := net.ParseIP(ipAddr)
- ecs.SourceNetmask = rule.v4BitMaskLen
- ecs.Address = ipv4Addr.Mask(ipv4Mask).To4()
- case 2:
- ipv6Mask := net.CIDRMask(int(rule.v6BitMaskLen), 128)
- ipv6Addr := net.ParseIP(ipAddr)
- ecs.SourceNetmask = rule.v6BitMaskLen
- ecs.Address = ipv6Addr.Mask(ipv6Mask).To16()
- }
- return nil
-}
-
-// Rewrite will alter the request EDNS0 subnet option
-func (rule *edns0SubnetRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result {
- result := RewriteIgnored
- o := setupEdns0Opt(r)
- found := false
- for _, s := range o.Option {
- switch e := s.(type) {
- case *dns.EDNS0_SUBNET:
- if rule.action == Replace || rule.action == Set {
- if rule.fillEcsData(w, r, e) == nil {
- result = RewriteDone
- }
- }
- found = true
- break
- }
- }
-
- // add option if not found
- if !found && (rule.action == Append || rule.action == Set) {
- o.SetDo()
- opt := dns.EDNS0_SUBNET{Code: dns.EDNS0SUBNET}
- if rule.fillEcsData(w, r, &opt) == nil {
- o.Option = append(o.Option, &opt)
- result = RewriteDone
- }
- }
-
- return result
-}
-
-// These are all defined actions.
-const (
- Replace = "replace"
- Set = "set"
- Append = "append"
-)
-
-// Supported local EDNS0 variables
-const (
- queryName = "{qname}"
- queryType = "{qtype}"
- clientIP = "{client_ip}"
- clientPort = "{client_port}"
- protocol = "{protocol}"
- serverIP = "{server_ip}"
- serverPort = "{server_port}"
-)
-
-// Subnet maximum bit mask length
-const (
- maxV4BitMaskLen = 32
- maxV6BitMaskLen = 128
-)
diff --git a/middleware/rewrite/name.go b/middleware/rewrite/name.go
deleted file mode 100644
index 6233197d6..000000000
--- a/middleware/rewrite/name.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package rewrite
-
-import (
- "github.com/coredns/coredns/middleware"
-
- "github.com/miekg/dns"
-)
-
-type nameRule struct {
- From, To string
-}
-
-func newNameRule(from, to string) (Rule, error) {
- return &nameRule{middleware.Name(from).Normalize(), middleware.Name(to).Normalize()}, nil
-}
-
-// Rewrite rewrites the the current request.
-func (rule *nameRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result {
- if rule.From == r.Question[0].Name {
- r.Question[0].Name = rule.To
- return RewriteDone
- }
- return RewriteIgnored
-}
diff --git a/middleware/rewrite/reverter.go b/middleware/rewrite/reverter.go
deleted file mode 100644
index 400fb5fff..000000000
--- a/middleware/rewrite/reverter.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package rewrite
-
-import "github.com/miekg/dns"
-
-// ResponseReverter reverses the operations done on the question section of a packet.
-// This is need because the client will otherwise disregards the response, i.e.
-// dig will complain with ';; Question section mismatch: got miek.nl/HINFO/IN'
-type ResponseReverter struct {
- dns.ResponseWriter
- original dns.Question
-}
-
-// NewResponseReverter returns a pointer to a new ResponseReverter.
-func NewResponseReverter(w dns.ResponseWriter, r *dns.Msg) *ResponseReverter {
- return &ResponseReverter{
- ResponseWriter: w,
- original: r.Question[0],
- }
-}
-
-// WriteMsg records the status code and calls the
-// underlying ResponseWriter's WriteMsg method.
-func (r *ResponseReverter) WriteMsg(res *dns.Msg) error {
- res.Question[0] = r.original
- return r.ResponseWriter.WriteMsg(res)
-}
-
-// Write is a wrapper that records the size of the message that gets written.
-func (r *ResponseReverter) Write(buf []byte) (int, error) {
- n, err := r.ResponseWriter.Write(buf)
- return n, err
-}
-
-// Hijack implements dns.Hijacker. It simply wraps the underlying
-// ResponseWriter's Hijack method if there is one, or returns an error.
-func (r *ResponseReverter) Hijack() {
- r.ResponseWriter.Hijack()
- return
-}
diff --git a/middleware/rewrite/rewrite.go b/middleware/rewrite/rewrite.go
deleted file mode 100644
index 44e8e43c7..000000000
--- a/middleware/rewrite/rewrite.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package rewrite
-
-import (
- "fmt"
- "strings"
-
- "github.com/coredns/coredns/middleware"
-
- "github.com/miekg/dns"
-
- "golang.org/x/net/context"
-)
-
-// Result is the result of a rewrite
-type Result int
-
-const (
- // RewriteIgnored is returned when rewrite is not done on request.
- RewriteIgnored Result = iota
- // RewriteDone is returned when rewrite is done on request.
- RewriteDone
- // RewriteStatus is returned when rewrite is not needed and status code should be set
- // for the request.
- RewriteStatus
-)
-
-// Rewrite is middleware to rewrite requests internally before being handled.
-type Rewrite struct {
- Next middleware.Handler
- Rules []Rule
- noRevert bool
-}
-
-// ServeDNS implements the middleware.Handler interface.
-func (rw Rewrite) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- wr := NewResponseReverter(w, r)
- for _, rule := range rw.Rules {
- switch result := rule.Rewrite(w, r); result {
- case RewriteDone:
- if rw.noRevert {
- return middleware.NextOrFailure(rw.Name(), rw.Next, ctx, w, r)
- }
- return middleware.NextOrFailure(rw.Name(), rw.Next, ctx, wr, r)
- case RewriteIgnored:
- break
- case RewriteStatus:
- // only valid for complex rules.
- // if cRule, ok := rule.(*ComplexRule); ok && cRule.Status != 0 {
- // return cRule.Status, nil
- // }
- }
- }
- return middleware.NextOrFailure(rw.Name(), rw.Next, ctx, w, r)
-}
-
-// Name implements the Handler interface.
-func (rw Rewrite) Name() string { return "rewrite" }
-
-// Rule describes a rewrite rule.
-type Rule interface {
- // Rewrite rewrites the current request.
- Rewrite(dns.ResponseWriter, *dns.Msg) Result
-}
-
-func newRule(args ...string) (Rule, error) {
- if len(args) == 0 {
- return nil, fmt.Errorf("no rule type specified for rewrite")
- }
-
- ruleType := strings.ToLower(args[0])
- if ruleType != "edns0" && len(args) != 3 {
- return nil, fmt.Errorf("%s rules must have exactly two arguments", ruleType)
- }
- switch ruleType {
- case "name":
- return newNameRule(args[1], args[2])
- case "class":
- return newClassRule(args[1], args[2])
- case "type":
- return newTypeRule(args[1], args[2])
- case "edns0":
- return newEdns0Rule(args[1:]...)
- default:
- return nil, fmt.Errorf("invalid rule type %q", args[0])
- }
-}
diff --git a/middleware/rewrite/rewrite_test.go b/middleware/rewrite/rewrite_test.go
deleted file mode 100644
index 39648711e..000000000
--- a/middleware/rewrite/rewrite_test.go
+++ /dev/null
@@ -1,532 +0,0 @@
-package rewrite
-
-import (
- "bytes"
- "reflect"
- "testing"
-
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func msgPrinter(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- w.WriteMsg(r)
- return 0, nil
-}
-
-func TestNewRule(t *testing.T) {
- tests := []struct {
- args []string
- shouldError bool
- expType reflect.Type
- }{
- {[]string{}, true, nil},
- {[]string{"foo"}, true, nil},
- {[]string{"name"}, true, nil},
- {[]string{"name", "a.com"}, true, nil},
- {[]string{"name", "a.com", "b.com", "c.com"}, true, nil},
- {[]string{"name", "a.com", "b.com"}, false, reflect.TypeOf(&nameRule{})},
- {[]string{"type"}, true, nil},
- {[]string{"type", "a"}, true, nil},
- {[]string{"type", "any", "a", "a"}, true, nil},
- {[]string{"type", "any", "a"}, false, reflect.TypeOf(&typeRule{})},
- {[]string{"type", "XY", "WV"}, true, nil},
- {[]string{"type", "ANY", "WV"}, true, nil},
- {[]string{"class"}, true, nil},
- {[]string{"class", "IN"}, true, nil},
- {[]string{"class", "ch", "in", "in"}, true, nil},
- {[]string{"class", "ch", "in"}, false, reflect.TypeOf(&classRule{})},
- {[]string{"class", "XY", "WV"}, true, nil},
- {[]string{"class", "IN", "WV"}, true, nil},
- {[]string{"edns0"}, true, nil},
- {[]string{"edns0", "local"}, true, nil},
- {[]string{"edns0", "local", "set"}, true, nil},
- {[]string{"edns0", "local", "set", "0xffee"}, true, nil},
- {[]string{"edns0", "local", "set", "65518", "abcdefg"}, false, reflect.TypeOf(&edns0LocalRule{})},
- {[]string{"edns0", "local", "set", "0xffee", "abcdefg"}, false, reflect.TypeOf(&edns0LocalRule{})},
- {[]string{"edns0", "local", "append", "0xffee", "abcdefg"}, false, reflect.TypeOf(&edns0LocalRule{})},
- {[]string{"edns0", "local", "replace", "0xffee", "abcdefg"}, false, reflect.TypeOf(&edns0LocalRule{})},
- {[]string{"edns0", "local", "foo", "0xffee", "abcdefg"}, true, nil},
- {[]string{"edns0", "local", "set", "0xffee", "0xabcdefg"}, true, nil},
- {[]string{"edns0", "nsid", "set", "junk"}, true, nil},
- {[]string{"edns0", "nsid", "set"}, false, reflect.TypeOf(&edns0NsidRule{})},
- {[]string{"edns0", "nsid", "append"}, false, reflect.TypeOf(&edns0NsidRule{})},
- {[]string{"edns0", "nsid", "replace"}, false, reflect.TypeOf(&edns0NsidRule{})},
- {[]string{"edns0", "nsid", "foo"}, true, nil},
- {[]string{"edns0", "local", "set", "0xffee", "{dummy}"}, true, nil},
- {[]string{"edns0", "local", "set", "0xffee", "{qname}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "set", "0xffee", "{qtype}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "set", "0xffee", "{client_ip}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "set", "0xffee", "{client_port}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "set", "0xffee", "{protocol}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "set", "0xffee", "{server_ip}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "set", "0xffee", "{server_port}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "append", "0xffee", "{dummy}"}, true, nil},
- {[]string{"edns0", "local", "append", "0xffee", "{qname}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "append", "0xffee", "{qtype}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "append", "0xffee", "{client_ip}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "append", "0xffee", "{client_port}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "append", "0xffee", "{protocol}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "append", "0xffee", "{server_ip}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "append", "0xffee", "{server_port}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "replace", "0xffee", "{dummy}"}, true, nil},
- {[]string{"edns0", "local", "replace", "0xffee", "{qname}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "replace", "0xffee", "{qtype}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "replace", "0xffee", "{client_ip}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "replace", "0xffee", "{client_port}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "replace", "0xffee", "{protocol}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "replace", "0xffee", "{server_ip}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "local", "replace", "0xffee", "{server_port}"}, false, reflect.TypeOf(&edns0VariableRule{})},
- {[]string{"edns0", "subnet", "set", "-1", "56"}, true, nil},
- {[]string{"edns0", "subnet", "set", "24", "-56"}, true, nil},
- {[]string{"edns0", "subnet", "set", "33", "56"}, true, nil},
- {[]string{"edns0", "subnet", "set", "24", "129"}, true, nil},
- {[]string{"edns0", "subnet", "set", "24", "56"}, false, reflect.TypeOf(&edns0SubnetRule{})},
- {[]string{"edns0", "subnet", "append", "24", "56"}, false, reflect.TypeOf(&edns0SubnetRule{})},
- {[]string{"edns0", "subnet", "replace", "24", "56"}, false, reflect.TypeOf(&edns0SubnetRule{})},
- }
-
- for i, tc := range tests {
- r, err := newRule(tc.args...)
- if err == nil && tc.shouldError {
- t.Errorf("Test %d: expected error but got success", i)
- } else if err != nil && !tc.shouldError {
- t.Errorf("Test %d: expected success but got error: %s", i, err)
- }
-
- if !tc.shouldError && reflect.TypeOf(r) != tc.expType {
- t.Errorf("Test %d: expected %q but got %q", i, tc.expType, r)
- }
- }
-}
-
-func TestRewrite(t *testing.T) {
- rules := []Rule{}
- r, _ := newNameRule("from.nl.", "to.nl.")
- rules = append(rules, r)
- r, _ = newClassRule("CH", "IN")
- rules = append(rules, r)
- r, _ = newTypeRule("ANY", "HINFO")
- rules = append(rules, r)
-
- rw := Rewrite{
- Next: middleware.HandlerFunc(msgPrinter),
- Rules: rules,
- noRevert: true,
- }
-
- tests := []struct {
- from string
- fromT uint16
- fromC uint16
- to string
- toT uint16
- toC uint16
- }{
- {"from.nl.", dns.TypeA, dns.ClassINET, "to.nl.", dns.TypeA, dns.ClassINET},
- {"a.nl.", dns.TypeA, dns.ClassINET, "a.nl.", dns.TypeA, dns.ClassINET},
- {"a.nl.", dns.TypeA, dns.ClassCHAOS, "a.nl.", dns.TypeA, dns.ClassINET},
- {"a.nl.", dns.TypeANY, dns.ClassINET, "a.nl.", dns.TypeHINFO, dns.ClassINET},
- // name is rewritten, type is not.
- {"from.nl.", dns.TypeANY, dns.ClassINET, "to.nl.", dns.TypeANY, dns.ClassINET},
- // name is not, type is, but class is, because class is the 2nd rule.
- {"a.nl.", dns.TypeANY, dns.ClassCHAOS, "a.nl.", dns.TypeANY, dns.ClassINET},
- }
-
- ctx := context.TODO()
- for i, tc := range tests {
- m := new(dns.Msg)
- m.SetQuestion(tc.from, tc.fromT)
- m.Question[0].Qclass = tc.fromC
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- rw.ServeDNS(ctx, rec, m)
-
- resp := rec.Msg
- if resp.Question[0].Name != tc.to {
- t.Errorf("Test %d: Expected Name to be %q but was %q", i, tc.to, resp.Question[0].Name)
- }
- if resp.Question[0].Qtype != tc.toT {
- t.Errorf("Test %d: Expected Type to be '%d' but was '%d'", i, tc.toT, resp.Question[0].Qtype)
- }
- if resp.Question[0].Qclass != tc.toC {
- t.Errorf("Test %d: Expected Class to be '%d' but was '%d'", i, tc.toC, resp.Question[0].Qclass)
- }
- }
-}
-
-func TestRewriteEDNS0Local(t *testing.T) {
- rw := Rewrite{
- Next: middleware.HandlerFunc(msgPrinter),
- noRevert: true,
- }
-
- tests := []struct {
- fromOpts []dns.EDNS0
- args []string
- toOpts []dns.EDNS0
- }{
- {
- []dns.EDNS0{},
- []string{"local", "set", "0xffee", "0xabcdef"},
- []dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte{0xab, 0xcd, 0xef}}},
- },
- {
- []dns.EDNS0{},
- []string{"local", "append", "0xffee", "abcdefghijklmnop"},
- []dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte("abcdefghijklmnop")}},
- },
- {
- []dns.EDNS0{},
- []string{"local", "replace", "0xffee", "abcdefghijklmnop"},
- []dns.EDNS0{},
- },
- {
- []dns.EDNS0{},
- []string{"nsid", "set"},
- []dns.EDNS0{&dns.EDNS0_NSID{Code: dns.EDNS0NSID, Nsid: ""}},
- },
- {
- []dns.EDNS0{},
- []string{"nsid", "append"},
- []dns.EDNS0{&dns.EDNS0_NSID{Code: dns.EDNS0NSID, Nsid: ""}},
- },
- {
- []dns.EDNS0{},
- []string{"nsid", "replace"},
- []dns.EDNS0{},
- },
- }
-
- ctx := context.TODO()
- for i, tc := range tests {
- m := new(dns.Msg)
- m.SetQuestion("example.com.", dns.TypeA)
- m.Question[0].Qclass = dns.ClassINET
-
- r, err := newEdns0Rule(tc.args...)
- if err != nil {
- t.Errorf("Error creating test rule: %s", err)
- continue
- }
- rw.Rules = []Rule{r}
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- rw.ServeDNS(ctx, rec, m)
-
- resp := rec.Msg
- o := resp.IsEdns0()
- if o == nil {
- t.Errorf("Test %d: EDNS0 options not set", i)
- continue
- }
- if !optsEqual(o.Option, tc.toOpts) {
- t.Errorf("Test %d: Expected %v but got %v", i, tc.toOpts, o)
- }
- }
-}
-
-func TestEdns0LocalMultiRule(t *testing.T) {
- rules := []Rule{}
- r, _ := newEdns0Rule("local", "replace", "0xffee", "abcdef")
- rules = append(rules, r)
- r, _ = newEdns0Rule("local", "set", "0xffee", "fedcba")
- rules = append(rules, r)
-
- rw := Rewrite{
- Next: middleware.HandlerFunc(msgPrinter),
- Rules: rules,
- noRevert: true,
- }
-
- tests := []struct {
- fromOpts []dns.EDNS0
- toOpts []dns.EDNS0
- }{
- {
- nil,
- []dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte("fedcba")}},
- },
- {
- []dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte("foobar")}},
- []dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte("abcdef")}},
- },
- }
-
- ctx := context.TODO()
- for i, tc := range tests {
- m := new(dns.Msg)
- m.SetQuestion("example.com.", dns.TypeA)
- m.Question[0].Qclass = dns.ClassINET
- if tc.fromOpts != nil {
- o := m.IsEdns0()
- if o == nil {
- m.SetEdns0(4096, true)
- o = m.IsEdns0()
- }
- o.Option = append(o.Option, tc.fromOpts...)
- }
- rec := dnsrecorder.New(&test.ResponseWriter{})
- rw.ServeDNS(ctx, rec, m)
-
- resp := rec.Msg
- o := resp.IsEdns0()
- if o == nil {
- t.Errorf("Test %d: EDNS0 options not set", i)
- continue
- }
- if !optsEqual(o.Option, tc.toOpts) {
- t.Errorf("Test %d: Expected %v but got %v", i, tc.toOpts, o)
- }
- }
-}
-
-func optsEqual(a, b []dns.EDNS0) bool {
- if len(a) != len(b) {
- return false
- }
- for i := range a {
- switch aa := a[i].(type) {
- case *dns.EDNS0_LOCAL:
- if bb, ok := b[i].(*dns.EDNS0_LOCAL); ok {
- if aa.Code != bb.Code {
- return false
- }
- if !bytes.Equal(aa.Data, bb.Data) {
- return false
- }
- } else {
- return false
- }
- case *dns.EDNS0_NSID:
- if bb, ok := b[i].(*dns.EDNS0_NSID); ok {
- if aa.Nsid != bb.Nsid {
- return false
- }
- } else {
- return false
- }
- case *dns.EDNS0_SUBNET:
- if bb, ok := b[i].(*dns.EDNS0_SUBNET); ok {
- if aa.Code != bb.Code {
- return false
- }
- if aa.Family != bb.Family {
- return false
- }
- if aa.SourceNetmask != bb.SourceNetmask {
- return false
- }
- if aa.SourceScope != bb.SourceScope {
- return false
- }
- if !bytes.Equal(aa.Address, bb.Address) {
- return false
- }
- if aa.DraftOption != bb.DraftOption {
- return false
- }
- } else {
- return false
- }
-
- default:
- return false
- }
- }
- return true
-}
-
-func TestRewriteEDNS0LocalVariable(t *testing.T) {
- rw := Rewrite{
- Next: middleware.HandlerFunc(msgPrinter),
- noRevert: true,
- }
-
- // test.ResponseWriter has the following values:
- // The remote will always be 10.240.0.1 and port 40212.
- // The local address is always 127.0.0.1 and port 53.
-
- tests := []struct {
- fromOpts []dns.EDNS0
- args []string
- toOpts []dns.EDNS0
- }{
- {
- []dns.EDNS0{},
- []string{"local", "set", "0xffee", "{qname}"},
- []dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte("example.com.")}},
- },
- {
- []dns.EDNS0{},
- []string{"local", "set", "0xffee", "{qtype}"},
- []dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte{0x00, 0x01}}},
- },
- {
- []dns.EDNS0{},
- []string{"local", "set", "0xffee", "{client_ip}"},
- []dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte{0x0A, 0xF0, 0x00, 0x01}}},
- },
- {
- []dns.EDNS0{},
- []string{"local", "set", "0xffee", "{client_port}"},
- []dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte{0x9D, 0x14}}},
- },
- {
- []dns.EDNS0{},
- []string{"local", "set", "0xffee", "{protocol}"},
- []dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte("udp")}},
- },
- {
- []dns.EDNS0{},
- []string{"local", "set", "0xffee", "{server_ip}"},
- []dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte{0x7F, 0x00, 0x00, 0x01}}},
- },
- {
- []dns.EDNS0{},
- []string{"local", "set", "0xffee", "{server_port}"},
- []dns.EDNS0{&dns.EDNS0_LOCAL{Code: 0xffee, Data: []byte{0x00, 0x35}}},
- },
- }
-
- ctx := context.TODO()
- for i, tc := range tests {
- m := new(dns.Msg)
- m.SetQuestion("example.com.", dns.TypeA)
- m.Question[0].Qclass = dns.ClassINET
-
- r, err := newEdns0Rule(tc.args...)
- if err != nil {
- t.Errorf("Error creating test rule: %s", err)
- continue
- }
- rw.Rules = []Rule{r}
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- rw.ServeDNS(ctx, rec, m)
-
- resp := rec.Msg
- o := resp.IsEdns0()
- if o == nil {
- t.Errorf("Test %d: EDNS0 options not set", i)
- continue
- }
- if !optsEqual(o.Option, tc.toOpts) {
- t.Errorf("Test %d: Expected %v but got %v", i, tc.toOpts, o)
- }
- }
-}
-
-func TestRewriteEDNS0Subnet(t *testing.T) {
- rw := Rewrite{
- Next: middleware.HandlerFunc(msgPrinter),
- noRevert: true,
- }
-
- tests := []struct {
- writer dns.ResponseWriter
- fromOpts []dns.EDNS0
- args []string
- toOpts []dns.EDNS0
- }{
- {
- &test.ResponseWriter{},
- []dns.EDNS0{},
- []string{"subnet", "set", "24", "56"},
- []dns.EDNS0{&dns.EDNS0_SUBNET{Code: 0x8,
- Family: 0x1,
- SourceNetmask: 0x18,
- SourceScope: 0x0,
- Address: []byte{0x0A, 0xF0, 0x00, 0x00},
- DraftOption: false}},
- },
- {
- &test.ResponseWriter{},
- []dns.EDNS0{},
- []string{"subnet", "set", "32", "56"},
- []dns.EDNS0{&dns.EDNS0_SUBNET{Code: 0x8,
- Family: 0x1,
- SourceNetmask: 0x20,
- SourceScope: 0x0,
- Address: []byte{0x0A, 0xF0, 0x00, 0x01},
- DraftOption: false}},
- },
- {
- &test.ResponseWriter{},
- []dns.EDNS0{},
- []string{"subnet", "set", "0", "56"},
- []dns.EDNS0{&dns.EDNS0_SUBNET{Code: 0x8,
- Family: 0x1,
- SourceNetmask: 0x0,
- SourceScope: 0x0,
- Address: []byte{0x00, 0x00, 0x00, 0x00},
- DraftOption: false}},
- },
- {
- &test.ResponseWriter6{},
- []dns.EDNS0{},
- []string{"subnet", "set", "24", "56"},
- []dns.EDNS0{&dns.EDNS0_SUBNET{Code: 0x8,
- Family: 0x2,
- SourceNetmask: 0x38,
- SourceScope: 0x0,
- Address: []byte{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
- DraftOption: false}},
- },
- {
- &test.ResponseWriter6{},
- []dns.EDNS0{},
- []string{"subnet", "set", "24", "128"},
- []dns.EDNS0{&dns.EDNS0_SUBNET{Code: 0x8,
- Family: 0x2,
- SourceNetmask: 0x80,
- SourceScope: 0x0,
- Address: []byte{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x42, 0x00, 0xff, 0xfe, 0xca, 0x4c, 0x65},
- DraftOption: false}},
- },
- {
- &test.ResponseWriter6{},
- []dns.EDNS0{},
- []string{"subnet", "set", "24", "0"},
- []dns.EDNS0{&dns.EDNS0_SUBNET{Code: 0x8,
- Family: 0x2,
- SourceNetmask: 0x0,
- SourceScope: 0x0,
- Address: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
- DraftOption: false}},
- },
- }
-
- ctx := context.TODO()
- for i, tc := range tests {
- m := new(dns.Msg)
- m.SetQuestion("example.com.", dns.TypeA)
- m.Question[0].Qclass = dns.ClassINET
-
- r, err := newEdns0Rule(tc.args...)
- if err != nil {
- t.Errorf("Error creating test rule: %s", err)
- continue
- }
- rw.Rules = []Rule{r}
- rec := dnsrecorder.New(tc.writer)
- rw.ServeDNS(ctx, rec, m)
-
- resp := rec.Msg
- o := resp.IsEdns0()
- if o == nil {
- t.Errorf("Test %d: EDNS0 options not set", i)
- continue
- }
- if !optsEqual(o.Option, tc.toOpts) {
- t.Errorf("Test %d: Expected %v but got %v", i, tc.toOpts, o)
- }
- }
-}
diff --git a/middleware/rewrite/setup.go b/middleware/rewrite/setup.go
deleted file mode 100644
index 156129f70..000000000
--- a/middleware/rewrite/setup.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package rewrite
-
-import (
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("rewrite", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- rewrites, err := rewriteParse(c)
- if err != nil {
- return middleware.Error("rewrite", err)
- }
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- return Rewrite{Next: next, Rules: rewrites}
- })
-
- return nil
-}
-
-func rewriteParse(c *caddy.Controller) ([]Rule, error) {
- var rules []Rule
-
- for c.Next() {
- args := c.RemainingArgs()
- rule, err := newRule(args...)
- if err != nil {
- return nil, err
- }
- rules = append(rules, rule)
- }
- return rules, nil
-}
diff --git a/middleware/rewrite/setup_test.go b/middleware/rewrite/setup_test.go
deleted file mode 100644
index 67ef88e18..000000000
--- a/middleware/rewrite/setup_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package rewrite
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestParse(t *testing.T) {
- c := caddy.NewTestController("dns", `rewrite`)
- _, err := rewriteParse(c)
- if err == nil {
- t.Errorf("Expected error but found nil for `rewrite`")
- }
- c = caddy.NewTestController("dns", `rewrite name`)
- _, err = rewriteParse(c)
- if err == nil {
- t.Errorf("Expected error but found nil for `rewrite name`")
- }
- c = caddy.NewTestController("dns", `rewrite name a.com b.com`)
- _, err = rewriteParse(c)
- if err != nil {
- t.Errorf("Expected success but found %s for `rewrite name a.com b.com`", err)
- }
-}
diff --git a/middleware/rewrite/testdata/testdir/empty b/middleware/rewrite/testdata/testdir/empty
deleted file mode 100644
index e69de29bb..000000000
--- a/middleware/rewrite/testdata/testdir/empty
+++ /dev/null
diff --git a/middleware/rewrite/testdata/testfile b/middleware/rewrite/testdata/testfile
deleted file mode 100644
index 7b4d68d70..000000000
--- a/middleware/rewrite/testdata/testfile
+++ /dev/null
@@ -1 +0,0 @@
-empty \ No newline at end of file
diff --git a/middleware/rewrite/type.go b/middleware/rewrite/type.go
deleted file mode 100644
index 58eedd51e..000000000
--- a/middleware/rewrite/type.go
+++ /dev/null
@@ -1,37 +0,0 @@
-// Package rewrite is middleware for rewriting requests internally to something different.
-package rewrite
-
-import (
- "fmt"
- "strings"
-
- "github.com/miekg/dns"
-)
-
-// typeRule is a type rewrite rule.
-type typeRule struct {
- fromType, toType uint16
-}
-
-func newTypeRule(fromS, toS string) (Rule, error) {
- var from, to uint16
- var ok bool
- if from, ok = dns.StringToType[strings.ToUpper(fromS)]; !ok {
- return nil, fmt.Errorf("invalid type %q", strings.ToUpper(fromS))
- }
- if to, ok = dns.StringToType[strings.ToUpper(toS)]; !ok {
- return nil, fmt.Errorf("invalid type %q", strings.ToUpper(toS))
- }
- return &typeRule{fromType: from, toType: to}, nil
-}
-
-// Rewrite rewrites the the current request.
-func (rule *typeRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result {
- if rule.fromType > 0 && rule.toType > 0 {
- if r.Question[0].Qtype == rule.fromType {
- r.Question[0].Qtype = rule.toType
- return RewriteDone
- }
- }
- return RewriteIgnored
-}
diff --git a/middleware/root/README.md b/middleware/root/README.md
deleted file mode 100644
index 23d35b2d3..000000000
--- a/middleware/root/README.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# root
-
-*root* simply specifies the root of where CoreDNS finds (e.g.) zone files.
-
-The default root is the current working directory of CoreDNS. A relative root path is relative to
-the current working directory.
-
-## Syntax
-
-~~~ txt
-root PATH
-~~~
-
-**PATH** is the directory to set as CoreDNS' root.
-
-## Examples
-
-Serve zone data (when the *file* middleware is used) from `/etc/coredns/zones`:
-
-~~~ txt
-root /etc/coredns/zones
-~~~
diff --git a/middleware/root/root.go b/middleware/root/root.go
deleted file mode 100644
index d03ecb8a4..000000000
--- a/middleware/root/root.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package root
-
-import (
- "log"
- "os"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("root", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- config := dnsserver.GetConfig(c)
-
- for c.Next() {
- if !c.NextArg() {
- return middleware.Error("root", c.ArgErr())
- }
- config.Root = c.Val()
- }
-
- // Check if root path exists
- _, err := os.Stat(config.Root)
- if err != nil {
- if os.IsNotExist(err) {
- // Allow this, because the folder might appear later.
- // But make sure the user knows!
- log.Printf("[WARNING] Root path does not exist: %s", config.Root)
- } else {
- return middleware.Error("root", c.Errf("unable to access root path '%s': %v", config.Root, err))
- }
- }
-
- return nil
-}
diff --git a/middleware/root/root_test.go b/middleware/root/root_test.go
deleted file mode 100644
index ea0e53b5e..000000000
--- a/middleware/root/root_test.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package root
-
-import (
- "fmt"
- "io/ioutil"
- "log"
- "os"
- "path/filepath"
- "strings"
- "testing"
-
- "github.com/coredns/coredns/core/dnsserver"
-
- "github.com/mholt/caddy"
-)
-
-func TestRoot(t *testing.T) {
- log.SetOutput(ioutil.Discard)
-
- // Predefined error substrings
- parseErrContent := "Error during parsing:"
- unableToAccessErrContent := "unable to access root path"
-
- existingDirPath, err := getTempDirPath()
- if err != nil {
- t.Fatalf("BeforeTest: Failed to find an existing directory for testing! Error was: %v", err)
- }
-
- nonExistingDir := filepath.Join(existingDirPath, "highly_unlikely_to_exist_dir")
-
- existingFile, err := ioutil.TempFile("", "root_test")
- if err != nil {
- t.Fatalf("BeforeTest: Failed to create temp file for testing! Error was: %v", err)
- }
- defer func() {
- existingFile.Close()
- os.Remove(existingFile.Name())
- }()
-
- inaccessiblePath := getInaccessiblePath(existingFile.Name())
-
- tests := []struct {
- input string
- shouldErr bool
- expectedRoot string // expected root, set to the controller. Empty for negative cases.
- expectedErrContent string // substring from the expected error. Empty for positive cases.
- }{
- // positive
- {
- fmt.Sprintf(`root %s`, nonExistingDir), false, nonExistingDir, "",
- },
- {
- fmt.Sprintf(`root %s`, existingDirPath), false, existingDirPath, "",
- },
- // negative
- {
- `root `, true, "", parseErrContent,
- },
- {
- fmt.Sprintf(`root %s`, inaccessiblePath), true, "", unableToAccessErrContent,
- },
- {
- fmt.Sprintf(`root {
- %s
- }`, existingDirPath), true, "", parseErrContent,
- },
- }
-
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- err := setup(c)
- cfg := dnsserver.GetConfig(c)
-
- if test.shouldErr && err == nil {
- t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input)
- }
-
- if err != nil {
- if !test.shouldErr {
- t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
- }
-
- if !strings.Contains(err.Error(), test.expectedErrContent) {
- t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input)
- }
- }
-
- // check root only if we are in a positive test.
- if !test.shouldErr && test.expectedRoot != cfg.Root {
- t.Errorf("Root not correctly set for input %s. Expected: %s, actual: %s", test.input, test.expectedRoot, cfg.Root)
- }
- }
-}
-
-// getTempDirPath returnes the path to the system temp directory. If it does not exists - an error is returned.
-func getTempDirPath() (string, error) {
- tempDir := os.TempDir()
- _, err := os.Stat(tempDir)
- if err != nil {
- return "", err
- }
- return tempDir, nil
-}
-
-func getInaccessiblePath(file string) string {
- return filepath.Join("C:", "file\x00name") // null byte in filename is not allowed on Windows AND unix
-}
diff --git a/middleware/secondary/README.md b/middleware/secondary/README.md
deleted file mode 100644
index d6cbe465a..000000000
--- a/middleware/secondary/README.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# secondary
-
-*secondary* enables serving a zone retrieved from a primary server.
-
-## Syntax
-
-~~~
-secondary [ZONES...]
-~~~
-
-* **ZONES** zones it should be authoritative for. If empty, the zones from the configuration block
- are used. Note that without a remote address to *get* the zone from, the above is not that useful.
-
-A working syntax would be:
-
-~~~
-secondary [zones...] {
- transfer from ADDRESS
- transfer to ADDRESS
- upstream ADDRESS...
-}
-~~~
-
-* `transfer from` specifies from which address to fetch the zone. It can be specified multiple times;
- if one does not work, another will be tried.
-* `transfer to` can be enabled to allow this secondary zone to be transferred again.
-* `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs)
- pointing to external names. This is only really useful when CoreDNS is configured as a proxy, for
- normal authoritative serving you don't need *or* want to use this. **ADDRESS** can be an IP
- address, and IP:port or a string pointing to a file that is structured as /etc/resolv.conf.
-
-## Examples
-
-Transfer `example.org` from 10.0.1.1, and if that fails try 10.1.2.1.
-
-~~~ corefile
-example.org {
- secondary {
- transfer from 10.0.1.1
- transfer from 10.1.2.1
- }
-}
-~~~
-
-Or re-export the retrieved zone to other secondaries.
-
-~~~ corefile
-. {
- secondary example.net {
- transfer from 10.1.2.1
- transfer to *
- }
-}
-~~~
diff --git a/middleware/secondary/secondary.go b/middleware/secondary/secondary.go
deleted file mode 100644
index da31d5171..000000000
--- a/middleware/secondary/secondary.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// Package secondary implements a secondary middleware.
-package secondary
-
-import "github.com/coredns/coredns/middleware/file"
-
-// Secondary implements a secondary middleware that allows CoreDNS to retrieve (via AXFR)
-// zone information from a primary server.
-type Secondary struct {
- file.File
-}
diff --git a/middleware/secondary/setup.go b/middleware/secondary/setup.go
deleted file mode 100644
index adba86f98..000000000
--- a/middleware/secondary/setup.go
+++ /dev/null
@@ -1,108 +0,0 @@
-package secondary
-
-import (
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/file"
- "github.com/coredns/coredns/middleware/pkg/dnsutil"
- "github.com/coredns/coredns/middleware/proxy"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("secondary", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- zones, err := secondaryParse(c)
- if err != nil {
- return middleware.Error("secondary", err)
- }
-
- // Add startup functions to retrieve the zone and keep it up to date.
- for _, n := range zones.Names {
- z := zones.Z[n]
- if len(z.TransferFrom) > 0 {
- c.OnStartup(func() error {
- z.StartupOnce.Do(func() {
- z.TransferIn()
- go func() {
- z.Update()
- }()
- })
- return nil
- })
- }
- }
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- return Secondary{file.File{Next: next, Zones: zones}}
- })
-
- return nil
-}
-
-func secondaryParse(c *caddy.Controller) (file.Zones, error) {
- z := make(map[string]*file.Zone)
- names := []string{}
- origins := []string{}
- prxy := proxy.Proxy{}
- for c.Next() {
-
- if c.Val() == "secondary" {
- // secondary [origin]
- origins = make([]string, len(c.ServerBlockKeys))
- copy(origins, c.ServerBlockKeys)
- args := c.RemainingArgs()
- if len(args) > 0 {
- origins = args
- }
- for i := range origins {
- origins[i] = middleware.Host(origins[i]).Normalize()
- z[origins[i]] = file.NewZone(origins[i], "stdin")
- names = append(names, origins[i])
- }
-
- for c.NextBlock() {
-
- t, f := []string{}, []string{}
- var e error
-
- switch c.Val() {
- case "transfer":
- t, f, e = file.TransferParse(c, true)
- if e != nil {
- return file.Zones{}, e
- }
- case "upstream":
- args := c.RemainingArgs()
- if len(args) == 0 {
- return file.Zones{}, c.ArgErr()
- }
- ups, err := dnsutil.ParseHostPortOrFile(args...)
- if err != nil {
- return file.Zones{}, err
- }
- prxy = proxy.NewLookup(ups)
- default:
- return file.Zones{}, c.Errf("unknown property '%s'", c.Val())
- }
-
- for _, origin := range origins {
- if t != nil {
- z[origin].TransferTo = append(z[origin].TransferTo, t...)
- }
- if f != nil {
- z[origin].TransferFrom = append(z[origin].TransferFrom, f...)
- }
- z[origin].Proxy = prxy
- }
- }
- }
- }
- return file.Zones{Z: z, Names: names}, nil
-}
diff --git a/middleware/secondary/setup_test.go b/middleware/secondary/setup_test.go
deleted file mode 100644
index bf2b203ad..000000000
--- a/middleware/secondary/setup_test.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package secondary
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestSecondaryParse(t *testing.T) {
- tests := []struct {
- inputFileRules string
- shouldErr bool
- transferFrom string
- zones []string
- }{
- {
- `secondary`,
- false, // TODO(miek): should actually be true, because without transfer lines this does not make sense
- "",
- nil,
- },
- {
- `secondary {
- transfer from 127.0.0.1
- transfer to 127.0.0.1
- }`,
- false,
- "127.0.0.1:53",
- nil,
- },
- {
- `secondary example.org {
- transfer from 127.0.0.1
- transfer to 127.0.0.1
- }`,
- false,
- "127.0.0.1:53",
- []string{"example.org."},
- },
- }
-
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.inputFileRules)
- s, err := secondaryParse(c)
-
- if err == nil && test.shouldErr {
- t.Fatalf("Test %d expected errors, but got no error", i)
- } else if err != nil && !test.shouldErr {
- t.Fatalf("Test %d expected no errors, but got '%v'", i, err)
- }
-
- for i, name := range test.zones {
- if x := s.Names[i]; x != name {
- t.Fatalf("Test %d zone names don't match expected %q, but got %q", i, name, x)
- }
- }
-
- // This is only set *iff* we have a zone (i.e. not in all tests above)
- for _, v := range s.Z {
- if x := v.TransferFrom[0]; x != test.transferFrom {
- t.Fatalf("Test %d transform from names don't match expected %q, but got %q", i, test.transferFrom, x)
- }
- }
- }
-}
diff --git a/middleware/test/doc.go b/middleware/test/doc.go
deleted file mode 100644
index e2f90262b..000000000
--- a/middleware/test/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package test contains helper functions for writing middleware tests.
-package test
diff --git a/middleware/test/file.go b/middleware/test/file.go
deleted file mode 100644
index f87300e55..000000000
--- a/middleware/test/file.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package test
-
-import (
- "io/ioutil"
- "os"
- "path/filepath"
-)
-
-// TempFile will create a temporary file on disk and returns the name and a cleanup function to remove it later.
-func TempFile(dir, content string) (string, func(), error) {
- f, err := ioutil.TempFile(dir, "go-test-tmpfile")
- if err != nil {
- return "", nil, err
- }
- if err := ioutil.WriteFile(f.Name(), []byte(content), 0644); err != nil {
- return "", nil, err
- }
- rmFunc := func() { os.Remove(f.Name()) }
- return f.Name(), rmFunc, nil
-}
-
-// WritePEMFiles creates a tmp dir with ca.pem, cert.pem, and key.pem and the func to remove it
-func WritePEMFiles(dir string) (string, func(), error) {
- tempDir, err := ioutil.TempDir(dir, "go-test-pemfiles")
- if err != nil {
- return "", nil, err
- }
-
- data := `-----BEGIN CERTIFICATE-----
-MIIC9zCCAd+gAwIBAgIJALGtqdMzpDemMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV
-BAMMB2t1YmUtY2EwHhcNMTYxMDE5MTU1NDI0WhcNNDQwMzA2MTU1NDI0WjASMRAw
-DgYDVQQDDAdrdWJlLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
-pa4Wu/WkpJNRr8pMVE6jjwzNUOx5mIyoDr8WILSxVQcEeyVPPmAqbmYXtVZO11p9
-jTzoEqF7Kgts3HVYGCk5abqbE14a8Ru/DmV5avU2hJ/NvSjtNi/O+V6SzCbg5yR9
-lBR53uADDlzuJEQT9RHq7A5KitFkx4vUcXnjOQCbDogWFoYuOgNEwJPy0Raz3NJc
-ViVfDqSJ0QHg02kCOMxcGFNRQ9F5aoW7QXZXZXD0tn3wLRlu4+GYyqt8fw5iNdLJ
-t79yKp8I+vMTmMPz4YKUO+eCl5EY10Qs7wvoG/8QNbjH01BRN3L8iDT2WfxdvjTu
-1RjPxFL92i+B7HZO7jGLfQIDAQABo1AwTjAdBgNVHQ4EFgQUZTrg+Xt87tkxDhlB
-gKk9FdTOW3IwHwYDVR0jBBgwFoAUZTrg+Xt87tkxDhlBgKk9FdTOW3IwDAYDVR0T
-BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEApB7JFVrZpGSOXNO3W7SlN6OCPXv9
-C7rIBc8rwOrzi2mZWcBmWheQrqBo8xHif2rlFNVQxtq3JcQ8kfg/m1fHeQ/Ygzel
-Z+U1OqozynDySBZdNn9i+kXXgAUCqDPp3hEQWe0os/RRpIwo9yOloBxdiX6S0NIf
-VB8n8kAynFPkH7pYrGrL1HQgDFCSfa4tUJ3+9sppnCu0pNtq5AdhYx9xFb2sn+8G
-xGbtCkhVk2VQ+BiCWnjYXJ6ZMzabP7wiOFDP9Pvr2ik22PRItsW/TLfHFXM1jDmc
-I1rs/VUGKzcJGVIWbHrgjP68CTStGAvKgbsTqw7aLXTSqtPw88N9XVSyRg==
------END CERTIFICATE-----`
- path := filepath.Join(tempDir, "ca.pem")
- if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
- return "", nil, err
- }
- data = `-----BEGIN CERTIFICATE-----
-MIICozCCAYsCCQCRlf5BrvPuqjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdr
-dWJlLWNhMB4XDTE2MTAxOTE2MDUxOFoXDTE3MTAxOTE2MDUxOFowFTETMBEGA1UE
-AwwKa3ViZS1hZG1pbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMTw
-a7wCFoiCad/N53aURfjrme+KR7FS0yf5Ur9OR/oM3BoS9stYu5Flzr35oL5T6t5G
-c2ey78mUs/Cs07psnjUdKH55bDpJSdG7zW9mXNyeLwIefFcj/38SS5NBSotmLo8u
-scJMGXeQpCQtfVuVJSP2bfU5u5d0KTLSg/Cor6UYonqrRB82HbOuuk8Wjaww4VHo
-nCq7X8o948V6HN5ZibQOgMMo+nf0wORREHBjvwc4W7ewbaTcfoe1VNAo/QnkqxTF
-ueMb2HxgghArqQSK8b44O05V0zrde25dVnmnte6sPjcV0plqMJ37jViISxsOPUFh
-/ZW7zbIM/7CMcDekCiECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAYZE8OxwRR7GR
-kdd5aIriDwWfcl56cq5ICyx87U8hAZhBxk46a6a901LZPzt3xKyWIFQSRj/NYiQ+
-/thjGLZI2lhkVgYtyAD4BNxDiuppQSCbkjY9tLVDdExGttEVN7+UYDWJBHy6X16Y
-xSG9FE3Dvp9LI89Nq8E3dRh+Q8wu52q9HaQXjS5YtzQOtDFKPBkihXu/c6gEHj4Y
-bZVk8rFiH8/CvcQxAuvNI3VVCFUKd2LeQtqwYQQ//qoiuA15krTq5Ut9eXJ8zxAw
-zhDEPP4FhY+Sz+y1yWirphl7A1aZwhXVPcfWIGqpQ3jzNwUeocbH27kuLh+U4hQo
-qeg10RdFnw==
------END CERTIFICATE-----`
- path = filepath.Join(tempDir, "cert.pem")
- if err = ioutil.WriteFile(path, []byte(data), 0644); err != nil {
- return "", nil, err
- }
-
- data = `-----BEGIN RSA PRIVATE KEY-----
-MIIEpgIBAAKCAQEAxPBrvAIWiIJp383ndpRF+OuZ74pHsVLTJ/lSv05H+gzcGhL2
-y1i7kWXOvfmgvlPq3kZzZ7LvyZSz8KzTumyeNR0ofnlsOklJ0bvNb2Zc3J4vAh58
-VyP/fxJLk0FKi2Yujy6xwkwZd5CkJC19W5UlI/Zt9Tm7l3QpMtKD8KivpRiieqtE
-HzYds666TxaNrDDhUeicKrtfyj3jxXoc3lmJtA6Awyj6d/TA5FEQcGO/Bzhbt7Bt
-pNx+h7VU0Cj9CeSrFMW54xvYfGCCECupBIrxvjg7TlXTOt17bl1Weae17qw+NxXS
-mWownfuNWIhLGw49QWH9lbvNsgz/sIxwN6QKIQIDAQABAoIBAQDCXq9V7ZGjxWMN
-OkFaLVkqJg3V91puztoMt+xNV8t+JTcOnOzrIXZuOFbl9PwLHPPP0SSRkm9LOvKl
-dU26zv0OWureeKSymia7U2mcqyC3tX+bzc7WinbeSYZBnc0e7AjD1EgpBcaU1TLL
-agIxY3A2oD9CKmrVPhZzTIZf/XztqTYjhvs5I2kBeT0imdYGpXkdndRyGX4I5/JQ
-fnp3Czj+AW3zX7RvVnXOh4OtIAcfoG9xoNyD5LOSlJkkX0MwTS8pEBeZA+A4nb+C
-ivjnOSgXWD+liisI+LpBgBbwYZ/E49x5ghZYrJt8QXSk7Bl/+UOyv6XZAm2mev6j
-RLAZtoABAoGBAP2P+1PoKOwsk+d/AmHqyTCUQm0UG18LOLB/5PyWfXs/6caDmdIe
-DZWeZWng1jUQLEadmoEw/CBY5+tPfHlzwzMNhT7KwUfIDQCIBoS7dzHYnwrJ3VZh
-qYA05cuGHAAHqwb6UWz3y6Pa4AEVSHX6CM83CAi9jdWZ1rdZybWG+qYBAoGBAMbV
-FsR/Ft+tK5ALgXGoG83TlmxzZYuZ1SnNje1OSdCQdMFCJB10gwoaRrw1ICzi40Xk
-ydJwV1upGz1om9ReDAD1zQM9artmQx6+TVLiVPALuARdZE70+NrA6w3ZvxUgJjdN
-ngvXUr+8SdvaYUAwFu7BulfJlwXjUS711hHW/KQhAoGBALY41QuV2mLwHlLNie7I
-hlGtGpe9TXZeYB0nrG6B0CfU5LJPPSotguG1dXhDpm138/nDpZeWlnrAqdsHwpKd
-yPhVjR51I7XsZLuvBdA50Q03egSM0c4UXXXPjh1XgaPb3uMi3YWMBwL4ducQXoS6
-bb5M9C8j2lxZNF+L3VPhbxwBAoGBAIEWDvX7XKpTDxkxnxRfA84ZNGusb5y2fsHp
-Bd+vGBUj8+kUO8Yzwm9op8vA4ebCVrMl2jGZZd3IaDryE1lIxZpJ+pPD5+tKdQEc
-o67P6jz+HrYWu+zW9klvPit71qasfKMi7Rza6oo4f+sQWFsH3ZucgpJD+pyD/Ez0
-pcpnPRaBAoGBANT/xgHBfIWt4U2rtmRLIIiZxKr+3mGnQdpA1J2BCh+/6AvrEx//
-E/WObVJXDnBdViu0L9abE9iaTToBVri4cmlDlZagLuKVR+TFTCN/DSlVZTDkqkLI
-8chzqtkH6b2b2R73hyRysWjsomys34ma3mEEPTX/aXeAF2MSZ/EWT9yL
------END RSA PRIVATE KEY-----`
- path = filepath.Join(tempDir, "key.pem")
- if err = ioutil.WriteFile(path, []byte(data), 0644); err != nil {
- return "", nil, err
- }
-
- rmFunc := func() { os.RemoveAll(tempDir) }
- return tempDir, rmFunc, nil
-}
diff --git a/middleware/test/file_test.go b/middleware/test/file_test.go
deleted file mode 100644
index ed86a8260..000000000
--- a/middleware/test/file_test.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package test
-
-import "testing"
-
-func TestTempFile(t *testing.T) {
- _, f, e := TempFile(".", "test")
- if e != nil {
- t.Fatalf("failed to create temp file: %s", e)
- }
- defer f()
-}
diff --git a/middleware/test/helpers.go b/middleware/test/helpers.go
deleted file mode 100644
index 35316dd38..000000000
--- a/middleware/test/helpers.go
+++ /dev/null
@@ -1,348 +0,0 @@
-package test
-
-import (
- "sort"
- "testing"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-type sect int
-
-const (
- // Answer is the answer section in an Msg.
- Answer sect = iota
- // Ns is the authoritative section in an Msg.
- Ns
- // Extra is the additional section in an Msg.
- Extra
-)
-
-// RRSet represents a list of RRs.
-type RRSet []dns.RR
-
-func (p RRSet) Len() int { return len(p) }
-func (p RRSet) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
-func (p RRSet) Less(i, j int) bool { return p[i].String() < p[j].String() }
-
-// Case represents a test case that encapsulates various data from a query and response.
-// Note that is the TTL of a record is 303 we don't compare it with the TTL.
-type Case struct {
- Qname string
- Qtype uint16
- Rcode int
- Do bool
- Answer []dns.RR
- Ns []dns.RR
- Extra []dns.RR
- Error error
-}
-
-// Msg returns a *dns.Msg embedded in c.
-func (c Case) Msg() *dns.Msg {
- m := new(dns.Msg)
- m.SetQuestion(dns.Fqdn(c.Qname), c.Qtype)
- if c.Do {
- o := new(dns.OPT)
- o.Hdr.Name = "."
- o.Hdr.Rrtype = dns.TypeOPT
- o.SetDo()
- o.SetUDPSize(4096)
- m.Extra = []dns.RR{o}
- }
- return m
-}
-
-// A returns an A record from rr. It panics on errors.
-func A(rr string) *dns.A { r, _ := dns.NewRR(rr); return r.(*dns.A) }
-
-// AAAA returns an AAAA record from rr. It panics on errors.
-func AAAA(rr string) *dns.AAAA { r, _ := dns.NewRR(rr); return r.(*dns.AAAA) }
-
-// CNAME returns a CNAME record from rr. It panics on errors.
-func CNAME(rr string) *dns.CNAME { r, _ := dns.NewRR(rr); return r.(*dns.CNAME) }
-
-// DNAME returns a DNAME record from rr. It panics on errors.
-func DNAME(rr string) *dns.DNAME { r, _ := dns.NewRR(rr); return r.(*dns.DNAME) }
-
-// SRV returns a SRV record from rr. It panics on errors.
-func SRV(rr string) *dns.SRV { r, _ := dns.NewRR(rr); return r.(*dns.SRV) }
-
-// SOA returns a SOA record from rr. It panics on errors.
-func SOA(rr string) *dns.SOA { r, _ := dns.NewRR(rr); return r.(*dns.SOA) }
-
-// NS returns an NS record from rr. It panics on errors.
-func NS(rr string) *dns.NS { r, _ := dns.NewRR(rr); return r.(*dns.NS) }
-
-// PTR returns a PTR record from rr. It panics on errors.
-func PTR(rr string) *dns.PTR { r, _ := dns.NewRR(rr); return r.(*dns.PTR) }
-
-// TXT returns a TXT record from rr. It panics on errors.
-func TXT(rr string) *dns.TXT { r, _ := dns.NewRR(rr); return r.(*dns.TXT) }
-
-// MX returns an MX record from rr. It panics on errors.
-func MX(rr string) *dns.MX { r, _ := dns.NewRR(rr); return r.(*dns.MX) }
-
-// RRSIG returns an RRSIG record from rr. It panics on errors.
-func RRSIG(rr string) *dns.RRSIG { r, _ := dns.NewRR(rr); return r.(*dns.RRSIG) }
-
-// NSEC returns an NSEC record from rr. It panics on errors.
-func NSEC(rr string) *dns.NSEC { r, _ := dns.NewRR(rr); return r.(*dns.NSEC) }
-
-// DNSKEY returns a DNSKEY record from rr. It panics on errors.
-func DNSKEY(rr string) *dns.DNSKEY { r, _ := dns.NewRR(rr); return r.(*dns.DNSKEY) }
-
-// DS returns a DS record from rr. It panics on errors.
-func DS(rr string) *dns.DS { r, _ := dns.NewRR(rr); return r.(*dns.DS) }
-
-// OPT returns an OPT record with UDP buffer size set to bufsize and the DO bit set to do.
-func OPT(bufsize int, do bool) *dns.OPT {
- o := new(dns.OPT)
- o.Hdr.Name = "."
- o.Hdr.Rrtype = dns.TypeOPT
- o.SetVersion(0)
- o.SetUDPSize(uint16(bufsize))
- if do {
- o.SetDo()
- }
- return o
-}
-
-// Header test if the header in resp matches the header as defined in tc.
-func Header(t *testing.T, tc Case, resp *dns.Msg) bool {
- if resp.Rcode != tc.Rcode {
- t.Errorf("rcode is %q, expected %q", dns.RcodeToString[resp.Rcode], dns.RcodeToString[tc.Rcode])
- return false
- }
-
- if len(resp.Answer) != len(tc.Answer) {
- t.Errorf("answer for %q contained %d results, %d expected", tc.Qname, len(resp.Answer), len(tc.Answer))
- return false
- }
- if len(resp.Ns) != len(tc.Ns) {
- t.Errorf("authority for %q contained %d results, %d expected", tc.Qname, len(resp.Ns), len(tc.Ns))
- return false
- }
- if len(resp.Extra) != len(tc.Extra) {
- t.Errorf("additional for %q contained %d results, %d expected", tc.Qname, len(resp.Extra), len(tc.Extra))
- return false
- }
- return true
-}
-
-// Section tests if the the section in tc matches rr.
-func Section(t *testing.T, tc Case, sec sect, rr []dns.RR) bool {
- section := []dns.RR{}
- switch sec {
- case 0:
- section = tc.Answer
- case 1:
- section = tc.Ns
- case 2:
- section = tc.Extra
- }
-
- for i, a := range rr {
- if a.Header().Name != section[i].Header().Name {
- t.Errorf("rr %d should have a Header Name of %q, but has %q", i, section[i].Header().Name, a.Header().Name)
- return false
- }
- // 303 signals: don't care what the ttl is.
- if section[i].Header().Ttl != 303 && a.Header().Ttl != section[i].Header().Ttl {
- if _, ok := section[i].(*dns.OPT); !ok {
- // we check edns0 bufize on this one
- t.Errorf("rr %d should have a Header TTL of %d, but has %d", i, section[i].Header().Ttl, a.Header().Ttl)
- return false
- }
- }
- if a.Header().Rrtype != section[i].Header().Rrtype {
- t.Errorf("rr %d should have a header rr type of %d, but has %d", i, section[i].Header().Rrtype, a.Header().Rrtype)
- return false
- }
-
- switch x := a.(type) {
- case *dns.SRV:
- if x.Priority != section[i].(*dns.SRV).Priority {
- t.Errorf("rr %d should have a Priority of %d, but has %d", i, section[i].(*dns.SRV).Priority, x.Priority)
- return false
- }
- if x.Weight != section[i].(*dns.SRV).Weight {
- t.Errorf("rr %d should have a Weight of %d, but has %d", i, section[i].(*dns.SRV).Weight, x.Weight)
- return false
- }
- if x.Port != section[i].(*dns.SRV).Port {
- t.Errorf("rr %d should have a Port of %d, but has %d", i, section[i].(*dns.SRV).Port, x.Port)
- return false
- }
- if x.Target != section[i].(*dns.SRV).Target {
- t.Errorf("rr %d should have a Target of %q, but has %q", i, section[i].(*dns.SRV).Target, x.Target)
- return false
- }
- case *dns.RRSIG:
- if x.TypeCovered != section[i].(*dns.RRSIG).TypeCovered {
- t.Errorf("rr %d should have a TypeCovered of %d, but has %d", i, section[i].(*dns.RRSIG).TypeCovered, x.TypeCovered)
- return false
- }
- if x.Labels != section[i].(*dns.RRSIG).Labels {
- t.Errorf("rr %d should have a Labels of %d, but has %d", i, section[i].(*dns.RRSIG).Labels, x.Labels)
- return false
- }
- if x.SignerName != section[i].(*dns.RRSIG).SignerName {
- t.Errorf("rr %d should have a SignerName of %s, but has %s", i, section[i].(*dns.RRSIG).SignerName, x.SignerName)
- return false
- }
- case *dns.NSEC:
- if x.NextDomain != section[i].(*dns.NSEC).NextDomain {
- t.Errorf("rr %d should have a NextDomain of %s, but has %s", i, section[i].(*dns.NSEC).NextDomain, x.NextDomain)
- return false
- }
- // TypeBitMap
- case *dns.A:
- if x.A.String() != section[i].(*dns.A).A.String() {
- t.Errorf("rr %d should have a Address of %q, but has %q", i, section[i].(*dns.A).A.String(), x.A.String())
- return false
- }
- case *dns.AAAA:
- if x.AAAA.String() != section[i].(*dns.AAAA).AAAA.String() {
- t.Errorf("rr %d should have a Address of %q, but has %q", i, section[i].(*dns.AAAA).AAAA.String(), x.AAAA.String())
- return false
- }
- case *dns.TXT:
- for j, txt := range x.Txt {
- if txt != section[i].(*dns.TXT).Txt[j] {
- t.Errorf("rr %d should have a Txt of %q, but has %q", i, section[i].(*dns.TXT).Txt[j], txt)
- return false
- }
- }
- case *dns.SOA:
- tt := section[i].(*dns.SOA)
- if x.Ns != tt.Ns {
- t.Errorf("SOA nameserver should be %q, but is %q", tt.Ns, x.Ns)
- return false
- }
- case *dns.PTR:
- tt := section[i].(*dns.PTR)
- if x.Ptr != tt.Ptr {
- t.Errorf("PTR ptr should be %q, but is %q", tt.Ptr, x.Ptr)
- return false
- }
- case *dns.CNAME:
- tt := section[i].(*dns.CNAME)
- if x.Target != tt.Target {
- t.Errorf("CNAME target should be %q, but is %q", tt.Target, x.Target)
- return false
- }
- case *dns.MX:
- tt := section[i].(*dns.MX)
- if x.Mx != tt.Mx {
- t.Errorf("MX Mx should be %q, but is %q", tt.Mx, x.Mx)
- return false
- }
- if x.Preference != tt.Preference {
- t.Errorf("MX Preference should be %q, but is %q", tt.Preference, x.Preference)
- return false
- }
- case *dns.NS:
- tt := section[i].(*dns.NS)
- if x.Ns != tt.Ns {
- t.Errorf("NS nameserver should be %q, but is %q", tt.Ns, x.Ns)
- return false
- }
- case *dns.OPT:
- tt := section[i].(*dns.OPT)
- if x.UDPSize() != tt.UDPSize() {
- t.Errorf("OPT UDPSize should be %d, but is %d", tt.UDPSize(), x.UDPSize())
- return false
- }
- if x.Do() != tt.Do() {
- t.Errorf("OPT DO should be %t, but is %t", tt.Do(), x.Do())
- return false
- }
- }
- }
- return true
-}
-
-// CNAMEOrder makes sure that CNAMES do not appear after their target records
-func CNAMEOrder(t *testing.T, res *dns.Msg) {
- for i, c := range res.Answer {
- if c.Header().Rrtype != dns.TypeCNAME {
- continue
- }
- for _, a := range res.Answer[:i] {
- if a.Header().Name != c.(*dns.CNAME).Target {
- continue
- }
- t.Errorf("CNAME found after target record\n")
- t.Logf("%v\n", res)
-
- }
- }
-}
-
-// SortAndCheck sorts resp and the checks the header and three sections against the testcase in tc.
-func SortAndCheck(t *testing.T, resp *dns.Msg, tc Case) {
- sort.Sort(RRSet(resp.Answer))
- sort.Sort(RRSet(resp.Ns))
- sort.Sort(RRSet(resp.Extra))
-
- if !Header(t, tc, resp) {
- t.Logf("%v\n", resp)
- return
- }
-
- if !Section(t, tc, Answer, resp.Answer) {
- t.Logf("%v\n", resp)
- return
- }
- if !Section(t, tc, Ns, resp.Ns) {
- t.Logf("%v\n", resp)
- return
-
- }
- if !Section(t, tc, Extra, resp.Extra) {
- t.Logf("%v\n", resp)
- return
- }
- return
-}
-
-// ErrorHandler returns a Handler that returns ServerFailure error when called.
-func ErrorHandler() Handler {
- return HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- m := new(dns.Msg)
- m.SetRcode(r, dns.RcodeServerFailure)
- w.WriteMsg(m)
- return dns.RcodeServerFailure, nil
- })
-}
-
-// NextHandler returns a Handler that returns rcode and err.
-func NextHandler(rcode int, err error) Handler {
- return HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- return rcode, err
- })
-}
-
-// Copied here to prevent an import cycle, so that we can define to above handlers.
-
-type (
- // HandlerFunc is a convenience type like dns.HandlerFunc, except
- // ServeDNS returns an rcode and an error.
- HandlerFunc func(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
-
- // Handler interface defines a middleware.
- Handler interface {
- ServeDNS(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
- Name() string
- }
-)
-
-// ServeDNS implements the Handler interface.
-func (f HandlerFunc) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- return f(ctx, w, r)
-}
-
-// Name implements the Handler interface.
-func (f HandlerFunc) Name() string { return "handlerfunc" }
diff --git a/middleware/test/responsewriter.go b/middleware/test/responsewriter.go
deleted file mode 100644
index 79eaa00f3..000000000
--- a/middleware/test/responsewriter.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package test
-
-import (
- "net"
-
- "github.com/miekg/dns"
-)
-
-// ResponseWriter is useful for writing tests. It uses some fixed values for the client. The
-// remote will always be 10.240.0.1 and port 40212. The local address is always 127.0.0.1 and
-// port 53.
-type ResponseWriter struct{}
-
-// LocalAddr returns the local address, always 127.0.0.1:53 (UDP).
-func (t *ResponseWriter) LocalAddr() net.Addr {
- ip := net.ParseIP("127.0.0.1")
- port := 53
- return &net.UDPAddr{IP: ip, Port: port, Zone: ""}
-}
-
-// RemoteAddr returns the remote address, always 10.240.0.1:40212 (UDP).
-func (t *ResponseWriter) RemoteAddr() net.Addr {
- ip := net.ParseIP("10.240.0.1")
- port := 40212
- return &net.UDPAddr{IP: ip, Port: port, Zone: ""}
-}
-
-// WriteMsg implement dns.ResponseWriter interface.
-func (t *ResponseWriter) WriteMsg(m *dns.Msg) error { return nil }
-
-// Write implement dns.ResponseWriter interface.
-func (t *ResponseWriter) Write(buf []byte) (int, error) { return len(buf), nil }
-
-// Close implement dns.ResponseWriter interface.
-func (t *ResponseWriter) Close() error { return nil }
-
-// TsigStatus implement dns.ResponseWriter interface.
-func (t *ResponseWriter) TsigStatus() error { return nil }
-
-// TsigTimersOnly implement dns.ResponseWriter interface.
-func (t *ResponseWriter) TsigTimersOnly(bool) { return }
-
-// Hijack implement dns.ResponseWriter interface.
-func (t *ResponseWriter) Hijack() { return }
-
-// RepsponseWrite6 returns fixed client and remote address in IPv6. The remote
-// address is always fe80::42:ff:feca:4c65 and port 40212. The local address
-// is always ::1 and port 53.
-type ResponseWriter6 struct {
- ResponseWriter
-}
-
-// LocalAddr returns the local address, always ::1, port 53 (UDP).
-func (t *ResponseWriter6) LocalAddr() net.Addr {
- return &net.UDPAddr{IP: net.ParseIP("::1"), Port: 53, Zone: ""}
-}
-
-// RemoteAddr returns the remote address, always fe80::42:ff:feca:4c65 port 40212 (UDP).
-func (t *ResponseWriter6) RemoteAddr() net.Addr {
- return &net.UDPAddr{IP: net.ParseIP("fe80::42:ff:feca:4c65"), Port: 40212, Zone: ""}
-}
diff --git a/middleware/test/server.go b/middleware/test/server.go
deleted file mode 100644
index eb39c7a5b..000000000
--- a/middleware/test/server.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package test
-
-import (
- "net"
- "sync"
- "time"
-
- "github.com/miekg/dns"
-)
-
-// TCPServer starts a DNS server with a TCP listener on laddr.
-func TCPServer(laddr string) (*dns.Server, string, error) {
- l, err := net.Listen("tcp", laddr)
- if err != nil {
- return nil, "", err
- }
-
- server := &dns.Server{Listener: l, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
-
- waitLock := sync.Mutex{}
- waitLock.Lock()
- server.NotifyStartedFunc = func() { waitLock.Unlock() }
-
- go func() {
- server.ActivateAndServe()
- l.Close()
- }()
-
- waitLock.Lock()
- return server, l.Addr().String(), nil
-}
-
-// UDPServer starts a DNS server with an UDP listener on laddr.
-func UDPServer(laddr string) (*dns.Server, string, error) {
- pc, err := net.ListenPacket("udp", laddr)
- if err != nil {
- return nil, "", err
- }
- server := &dns.Server{PacketConn: pc, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
-
- waitLock := sync.Mutex{}
- waitLock.Lock()
- server.NotifyStartedFunc = func() { waitLock.Unlock() }
-
- go func() {
- server.ActivateAndServe()
- pc.Close()
- }()
-
- waitLock.Lock()
- return server, pc.LocalAddr().String(), nil
-}
diff --git a/middleware/tls/README.md b/middleware/tls/README.md
deleted file mode 100644
index a5c02c4c7..000000000
--- a/middleware/tls/README.md
+++ /dev/null
@@ -1,52 +0,0 @@
-# tls
-
-*tls* allows you to configure the server certificates for the TLS and gRPC servers.
-For other types of servers it is ignored.
-
-CoreDNS supports queries that are encrypted using TLS (DNS over Transport Layer Security, RFC 7858)
-or are using gRPC (https://grpc.io/, not an IETF standard). Normally DNS traffic isn't encrypted at
-all (DNSSEC only signs resource records).
-
-The *proxy* middleware also support gRPC (`protocol gRPC`), meaning you can chain CoreDNS servers
-using this protocol.
-
-The *tls* "middleware" allows you to configure the cryptographic keys that are needed for both
-DNS-over-TLS and DNS-over-gRPC. If the `tls` directive is omitted, then no encryption takes place.
-
-The gRPC protobuffer is defined in `pb/dns.proto`. It defines the proto as a simple wrapper for the
-wire data of a DNS message.
-
-## Syntax
-
-~~~ txt
-tls CERT KEY CA
-~~~
-
-## Examples
-
-Start a DNS-over-TLS server that picks up incoming DNS-over-TLS queries on port 5553 and uses the
-nameservers defined in `/etc/resolv.conf` to resolve the query. This proxy path uses plain old DNS.
-
-~~~
-tls://.:5553 {
- tls cert.pem key.pem ca.pem
- proxy . /etc/resolv.conf
-}
-~~~
-
-Start a DNS-over-gRPC server that is similar to the previous example, but using DNS-over-gRPC for
-incoming queries.
-
-~~~
-grpc://. {
- tls cert.pem key.pem ca.pem
- proxy . /etc/resolv.conf
-}
-~~~
-
-Only Knot DNS' `kdig` supports DNS-over-TLS queries, no command line client supports gRPC making
-debugging these transports harder than it should be.
-
-## Also See
-
-RFC 7858 and https://grpc.io.
diff --git a/middleware/tls/tls.go b/middleware/tls/tls.go
deleted file mode 100644
index 55f2856c1..000000000
--- a/middleware/tls/tls.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package tls
-
-import (
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
- "github.com/coredns/coredns/middleware/pkg/tls"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("tls", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- config := dnsserver.GetConfig(c)
-
- if config.TLSConfig != nil {
- return middleware.Error("tls", c.Errf("TLS already configured for this server instance"))
- }
-
- for c.Next() {
- args := c.RemainingArgs()
- if len(args) != 3 {
- return middleware.Error("tls", c.ArgErr())
- }
- tls, err := tls.NewTLSConfig(args[0], args[1], args[2])
- if err != nil {
- return middleware.Error("tls", err)
- }
- config.TLSConfig = tls
- }
- return nil
-}
diff --git a/middleware/tls/tls_test.go b/middleware/tls/tls_test.go
deleted file mode 100644
index 2374d772c..000000000
--- a/middleware/tls/tls_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package tls
-
-import (
- "io/ioutil"
- "log"
- "strings"
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestTLS(t *testing.T) {
- log.SetOutput(ioutil.Discard)
-
- tests := []struct {
- input string
- shouldErr bool
- expectedRoot string // expected root, set to the controller. Empty for negative cases.
- expectedErrContent string // substring from the expected error. Empty for positive cases.
- }{
- // positive
- // negative
- }
-
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- err := setup(c)
- //cfg := dnsserver.GetConfig(c)
-
- if test.shouldErr && err == nil {
- t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input)
- }
-
- if err != nil {
- if !test.shouldErr {
- t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
- }
-
- if !strings.Contains(err.Error(), test.expectedErrContent) {
- t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input)
- }
- }
- }
-}
diff --git a/middleware/trace/README.md b/middleware/trace/README.md
deleted file mode 100644
index aa157e1e2..000000000
--- a/middleware/trace/README.md
+++ /dev/null
@@ -1,73 +0,0 @@
-# trace
-
-This module enables OpenTracing-based tracing of DNS requests as they go through the
-middleware chain.
-
-## Syntax
-
-The simplest form is just:
-
-~~~
-trace [ENDPOINT-TYPE] [ENDPOINT]
-~~~
-
-* **ENDPOINT-TYPE** is the type of tracing destination. Currently only `zipkin` is supported
- and that is what it defaults to.
-* **ENDPOINT** is the tracing destination, and defaults to `localhost:9411`. For Zipkin, if
- ENDPOINT does not begin with `http`, then it will be transformed to `http://ENDPOINT/api/v1/spans`.
-
-With this form, all queries will be traced.
-
-Additional features can be enabled with this syntax:
-
-~~~
-trace [ENDPOINT-TYPE] [ENDPOINT] {
- every AMOUNT
- service NAME
- client_server
-}
-~~~
-
-* `every` **AMOUNT** will only trace one query of each AMOUNT queries. For example, to trace 1 in every
- 100 queries, use AMOUNT of 100. The default is 1.
-* `service` **NAME** allows you to specify the service name reported to the tracing server.
- Default is `coredns`.
-* `client_server` will enable the `ClientServerSameSpan` OpenTracing feature.
-
-## Zipkin
-You can run Zipkin on a Docker host like this:
-
-```
-docker run -d -p 9411:9411 openzipkin/zipkin
-```
-
-## Examples
-
-Use an alternative Zipkin address:
-
-~~~
-trace tracinghost:9253
-~~~
-
-or
-
-~~~
-trace zipkin tracinghost:9253
-~~~
-
-If for some reason you are using an API reverse proxy or something and need to remap
-the standard Zipkin URL you can do something like:
-
-~~~
-trace http://tracinghost:9411/zipkin/api/v1/spans
-~~~
-
-Trace one query every 10000 queries, rename the service, and enable same span:
-
-~~~
-trace tracinghost:9411 {
- every 10000
- service dnsproxy
- client_server
-}
-~~~
diff --git a/middleware/trace/setup.go b/middleware/trace/setup.go
deleted file mode 100644
index 601472fee..000000000
--- a/middleware/trace/setup.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package trace
-
-import (
- "fmt"
- "strconv"
- "strings"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("trace", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- t, err := traceParse(c)
- if err != nil {
- return middleware.Error("trace", err)
- }
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- t.Next = next
- return t
- })
-
- c.OnStartup(t.OnStartup)
-
- return nil
-}
-
-func traceParse(c *caddy.Controller) (*trace, error) {
- var (
- tr = &trace{Endpoint: defEP, EndpointType: defEpType, every: 1, serviceName: defServiceName}
- err error
- )
-
- cfg := dnsserver.GetConfig(c)
- tr.ServiceEndpoint = cfg.ListenHost + ":" + cfg.Port
- for c.Next() { // trace
- var err error
- args := c.RemainingArgs()
- switch len(args) {
- case 0:
- tr.Endpoint, err = normalizeEndpoint(tr.EndpointType, defEP)
- case 1:
- tr.Endpoint, err = normalizeEndpoint(defEpType, args[0])
- case 2:
- tr.EndpointType = strings.ToLower(args[0])
- tr.Endpoint, err = normalizeEndpoint(tr.EndpointType, args[1])
- default:
- err = c.ArgErr()
- }
- if err != nil {
- return tr, err
- }
- for c.NextBlock() {
- switch c.Val() {
- case "every":
- args := c.RemainingArgs()
- if len(args) != 1 {
- return nil, c.ArgErr()
- }
- tr.every, err = strconv.ParseUint(args[0], 10, 64)
- if err != nil {
- return nil, err
- }
- case "service":
- args := c.RemainingArgs()
- if len(args) != 1 {
- return nil, c.ArgErr()
- }
- tr.serviceName = args[0]
- case "client_server":
- args := c.RemainingArgs()
- if len(args) > 1 {
- return nil, c.ArgErr()
- }
- tr.clientServer = true
- if len(args) == 1 {
- tr.clientServer, err = strconv.ParseBool(args[0])
- }
- if err != nil {
- return nil, err
- }
- }
- }
- }
- return tr, err
-}
-
-func normalizeEndpoint(epType, ep string) (string, error) {
- switch epType {
- case "zipkin":
- if !strings.Contains(ep, "http") {
- ep = "http://" + ep + "/api/v1/spans"
- }
- return ep, nil
- default:
- return "", fmt.Errorf("tracing endpoint type '%s' is not supported", epType)
- }
-}
-
-const (
- defEP = "localhost:9411"
- defEpType = "zipkin"
- defServiceName = "coredns"
-)
diff --git a/middleware/trace/setup_test.go b/middleware/trace/setup_test.go
deleted file mode 100644
index 3c12b76e4..000000000
--- a/middleware/trace/setup_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package trace
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestTraceParse(t *testing.T) {
- tests := []struct {
- input string
- shouldErr bool
- endpoint string
- every uint64
- serviceName string
- clientServer bool
- }{
- // oks
- {`trace`, false, "http://localhost:9411/api/v1/spans", 1, `coredns`, false},
- {`trace localhost:1234`, false, "http://localhost:1234/api/v1/spans", 1, `coredns`, false},
- {`trace http://localhost:1234/somewhere/else`, false, "http://localhost:1234/somewhere/else", 1, `coredns`, false},
- {`trace zipkin localhost:1234`, false, "http://localhost:1234/api/v1/spans", 1, `coredns`, false},
- {`trace zipkin http://localhost:1234/somewhere/else`, false, "http://localhost:1234/somewhere/else", 1, `coredns`, false},
- {"trace {\n every 100\n}", false, "http://localhost:9411/api/v1/spans", 100, `coredns`, false},
- {"trace {\n every 100\n service foobar\nclient_server\n}", false, "http://localhost:9411/api/v1/spans", 100, `foobar`, true},
- {"trace {\n every 2\n client_server true\n}", false, "http://localhost:9411/api/v1/spans", 2, `coredns`, true},
- {"trace {\n client_server false\n}", false, "http://localhost:9411/api/v1/spans", 1, `coredns`, false},
- // fails
- {`trace footype localhost:4321`, true, "", 1, "", false},
- {"trace {\n every 2\n client_server junk\n}", true, "", 1, "", false},
- }
- for i, test := range tests {
- c := caddy.NewTestController("dns", test.input)
- m, err := traceParse(c)
- if test.shouldErr && err == nil {
- t.Errorf("Test %v: Expected error but found nil", i)
- continue
- } else if !test.shouldErr && err != nil {
- t.Errorf("Test %v: Expected no error but found error: %v", i, err)
- continue
- }
-
- if test.shouldErr {
- continue
- }
-
- if test.endpoint != m.Endpoint {
- t.Errorf("Test %v: Expected endpoint %s but found: %s", i, test.endpoint, m.Endpoint)
- }
- if test.every != m.every {
- t.Errorf("Test %v: Expected every %d but found: %d", i, test.every, m.every)
- }
- if test.serviceName != m.serviceName {
- t.Errorf("Test %v: Expected service name %s but found: %s", i, test.serviceName, m.serviceName)
- }
- if test.clientServer != m.clientServer {
- t.Errorf("Test %v: Expected client_server %t but found: %t", i, test.clientServer, m.clientServer)
- }
- }
-}
diff --git a/middleware/trace/trace.go b/middleware/trace/trace.go
deleted file mode 100644
index 86561871a..000000000
--- a/middleware/trace/trace.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// Package trace implements OpenTracing-based tracing
-package trace
-
-import (
- "fmt"
- "sync"
- "sync/atomic"
-
- "github.com/coredns/coredns/middleware"
- // Plugin the trace package.
- _ "github.com/coredns/coredns/middleware/pkg/trace"
-
- "github.com/miekg/dns"
- ot "github.com/opentracing/opentracing-go"
- zipkin "github.com/openzipkin/zipkin-go-opentracing"
- "golang.org/x/net/context"
-)
-
-type trace struct {
- Next middleware.Handler
- ServiceEndpoint string
- Endpoint string
- EndpointType string
- tracer ot.Tracer
- serviceName string
- clientServer bool
- every uint64
- count uint64
- Once sync.Once
-}
-
-func (t *trace) Tracer() ot.Tracer {
- return t.tracer
-}
-
-// OnStartup sets up the tracer
-func (t *trace) OnStartup() error {
- var err error
- t.Once.Do(func() {
- switch t.EndpointType {
- case "zipkin":
- err = t.setupZipkin()
- default:
- err = fmt.Errorf("unknown endpoint type: %s", t.EndpointType)
- }
- })
- return err
-}
-
-func (t *trace) setupZipkin() error {
-
- collector, err := zipkin.NewHTTPCollector(t.Endpoint)
- if err != nil {
- return err
- }
-
- recorder := zipkin.NewRecorder(collector, false, t.ServiceEndpoint, t.serviceName)
- t.tracer, err = zipkin.NewTracer(recorder, zipkin.ClientServerSameSpan(t.clientServer))
-
- return err
-}
-
-// Name implements the Handler interface.
-func (t *trace) Name() string {
- return "trace"
-}
-
-// ServeDNS implements the middleware.Handle interface.
-func (t *trace) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- trace := false
- if t.every > 0 {
- queryNr := atomic.AddUint64(&t.count, 1)
-
- if queryNr%t.every == 0 {
- trace = true
- }
- }
- if span := ot.SpanFromContext(ctx); span == nil && trace {
- span := t.Tracer().StartSpan("servedns")
- defer span.Finish()
- ctx = ot.ContextWithSpan(ctx, span)
- }
- return middleware.NextOrFailure(t.Name(), t.Next, ctx, w, r)
-}
diff --git a/middleware/trace/trace_test.go b/middleware/trace/trace_test.go
deleted file mode 100644
index 06b2aafcf..000000000
--- a/middleware/trace/trace_test.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package trace
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-// createTestTrace creates a trace middleware to be used in tests
-func createTestTrace(config string) (*caddy.Controller, *trace, error) {
- c := caddy.NewTestController("dns", config)
- m, err := traceParse(c)
- return c, m, err
-}
-
-func TestTrace(t *testing.T) {
- _, m, err := createTestTrace(`trace`)
- if err != nil {
- t.Errorf("Error parsing test input: %s", err)
- return
- }
- if m.Name() != "trace" {
- t.Errorf("Wrong name from GetName: %s", m.Name())
- }
- err = m.OnStartup()
- if err != nil {
- t.Errorf("Error starting tracing middleware: %s", err)
- return
- }
- if m.Tracer() == nil {
- t.Errorf("Error, no tracer created")
- }
-}
diff --git a/middleware/whoami/README.md b/middleware/whoami/README.md
deleted file mode 100644
index 4b56a2e77..000000000
--- a/middleware/whoami/README.md
+++ /dev/null
@@ -1,44 +0,0 @@
-# whoami
-
-*whoami* returns your resolver's local IP address, port and transport. Your IP address is returned
- in the additional section as either an A or AAAA record.
-
-The reply always has an empty answer section. The port and transport are included in the additional
-section as a SRV record, transport can be "tcp" or "udp".
-
-~~~ txt
-._<transport>.qname. 0 IN SRV 0 0 <port> .
-~~~
-
-If CoreDNS can't find a Corefile on startup this is the *default* middleware that gets loaded. As
-such it can be used to check that CoreDNS is responding to queries. Other than that this middleware
-is of limited use in production.
-
-The *whoami* middleware will respond to every A or AAAA query, regardless of the query name.
-
-## Syntax
-
-~~~ txt
-whoami
-~~~
-
-## Examples
-
-Start a server on the default port and load the *whoami* middleware.
-
-~~~ corefile
-. {
- whoami
-}
-~~~
-
-When queried for "example.org A", CoreDNS will respond with:
-
-~~~ txt
-;; QUESTION SECTION:
-;example.org. IN A
-
-;; ADDITIONAL SECTION:
-example.org. 0 IN A 10.240.0.1
-_udp.example.org. 0 IN SRV 0 0 40212
-~~~
diff --git a/middleware/whoami/setup.go b/middleware/whoami/setup.go
deleted file mode 100644
index e11ac6567..000000000
--- a/middleware/whoami/setup.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package whoami
-
-import (
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/middleware"
-
- "github.com/mholt/caddy"
-)
-
-func init() {
- caddy.RegisterPlugin("whoami", caddy.Plugin{
- ServerType: "dns",
- Action: setupWhoami,
- })
-}
-
-func setupWhoami(c *caddy.Controller) error {
- c.Next() // 'whoami'
- if c.NextArg() {
- return middleware.Error("whoami", c.ArgErr())
- }
-
- dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
- return Whoami{}
- })
-
- return nil
-}
diff --git a/middleware/whoami/setup_test.go b/middleware/whoami/setup_test.go
deleted file mode 100644
index 73db67d88..000000000
--- a/middleware/whoami/setup_test.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package whoami
-
-import (
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestSetupWhoami(t *testing.T) {
- c := caddy.NewTestController("dns", `whoami`)
- if err := setupWhoami(c); err != nil {
- t.Fatalf("Expected no errors, but got: %v", err)
- }
-
- c = caddy.NewTestController("dns", `whoami example.org`)
- if err := setupWhoami(c); err == nil {
- t.Fatalf("Expected errors, but got: %v", err)
- }
-}
diff --git a/middleware/whoami/whoami.go b/middleware/whoami/whoami.go
deleted file mode 100644
index 6b5ed332a..000000000
--- a/middleware/whoami/whoami.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// Package whoami implements a middleware that returns details about the resolving
-// querying it.
-package whoami
-
-import (
- "net"
- "strconv"
-
- "github.com/coredns/coredns/request"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// Whoami is a middleware that returns your IP address, port and the protocol used for connecting
-// to CoreDNS.
-type Whoami struct{}
-
-// ServeDNS implements the middleware.Handler interface.
-func (wh Whoami) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- state := request.Request{W: w, Req: r}
-
- a := new(dns.Msg)
- a.SetReply(r)
- a.Compress = true
- a.Authoritative = true
-
- ip := state.IP()
- var rr dns.RR
-
- switch state.Family() {
- case 1:
- rr = new(dns.A)
- rr.(*dns.A).Hdr = dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeA, Class: state.QClass()}
- rr.(*dns.A).A = net.ParseIP(ip).To4()
- case 2:
- rr = new(dns.AAAA)
- rr.(*dns.AAAA).Hdr = dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeAAAA, Class: state.QClass()}
- rr.(*dns.AAAA).AAAA = net.ParseIP(ip)
- }
-
- srv := new(dns.SRV)
- srv.Hdr = dns.RR_Header{Name: "_" + state.Proto() + "." + state.QName(), Rrtype: dns.TypeSRV, Class: state.QClass()}
- port, _ := strconv.Atoi(state.Port())
- srv.Port = uint16(port)
- srv.Target = "."
-
- a.Extra = []dns.RR{rr, srv}
-
- state.SizeAndDo(a)
- w.WriteMsg(a)
-
- return 0, nil
-}
-
-// Name implements the Handler interface.
-func (wh Whoami) Name() string { return "whoami" }
diff --git a/middleware/whoami/whoami_test.go b/middleware/whoami/whoami_test.go
deleted file mode 100644
index d9357e497..000000000
--- a/middleware/whoami/whoami_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package whoami
-
-import (
- "testing"
-
- "github.com/coredns/coredns/middleware/pkg/dnsrecorder"
- "github.com/coredns/coredns/middleware/test"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-func TestWhoami(t *testing.T) {
- wh := Whoami{}
-
- tests := []struct {
- qname string
- qtype uint16
- expectedCode int
- expectedReply []string // ownernames for the records in the additional section.
- expectedErr error
- }{
- {
- qname: "example.org",
- qtype: dns.TypeA,
- expectedCode: dns.RcodeSuccess,
- expectedReply: []string{"example.org.", "_udp.example.org."},
- expectedErr: nil,
- },
- }
-
- ctx := context.TODO()
-
- for i, tc := range tests {
- req := new(dns.Msg)
- req.SetQuestion(dns.Fqdn(tc.qname), tc.qtype)
-
- rec := dnsrecorder.New(&test.ResponseWriter{})
- code, err := wh.ServeDNS(ctx, rec, req)
-
- if err != tc.expectedErr {
- t.Errorf("Test %d: Expected error %v, but got %v", i, tc.expectedErr, err)
- }
- if code != int(tc.expectedCode) {
- t.Errorf("Test %d: Expected status code %d, but got %d", i, tc.expectedCode, code)
- }
- if len(tc.expectedReply) != 0 {
- for i, expected := range tc.expectedReply {
- actual := rec.Msg.Extra[i].Header().Name
- if actual != expected {
- t.Errorf("Test %d: Expected answer %s, but got %s", i, expected, actual)
- }
- }
- }
- }
-}