aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/plugin/zplugin.go2
-rw-r--r--coremain/run.go7
-rw-r--r--plugin.cfg2
-rw-r--r--plugin/deprecated/setup.go2
-rw-r--r--plugin/proxy/README.md49
-rw-r--r--plugin/proxy/dnstap_test.go1
-rw-r--r--plugin/proxy/google.go219
-rw-r--r--plugin/proxy/google_rr.go89
-rw-r--r--plugin/proxy/google_test.go5
-rw-r--r--plugin/proxy/proxy.go8
-rw-r--r--plugin/proxy/upstream.go7
-rw-r--r--plugin/reverse/OWNERS2
-rw-r--r--plugin/reverse/README.md96
-rw-r--r--plugin/reverse/network.go87
-rw-r--r--plugin/reverse/network_test.go135
-rw-r--r--plugin/reverse/reverse.go108
-rw-r--r--plugin/reverse/reverse_test.go70
-rw-r--r--plugin/reverse/setup.go154
-rw-r--r--plugin/reverse/setup_test.go195
-rw-r--r--test/reverse_test.go77
20 files changed, 9 insertions, 1306 deletions
diff --git a/core/plugin/zplugin.go b/core/plugin/zplugin.go
index d22904fbd..d1c0aaa73 100644
--- a/core/plugin/zplugin.go
+++ b/core/plugin/zplugin.go
@@ -10,6 +10,7 @@ import (
_ "github.com/coredns/coredns/plugin/cache"
_ "github.com/coredns/coredns/plugin/chaos"
_ "github.com/coredns/coredns/plugin/debug"
+ _ "github.com/coredns/coredns/plugin/deprecated"
_ "github.com/coredns/coredns/plugin/dnssec"
_ "github.com/coredns/coredns/plugin/dnstap"
_ "github.com/coredns/coredns/plugin/erratic"
@@ -28,7 +29,6 @@ import (
_ "github.com/coredns/coredns/plugin/pprof"
_ "github.com/coredns/coredns/plugin/proxy"
_ "github.com/coredns/coredns/plugin/reload"
- _ "github.com/coredns/coredns/plugin/reverse"
_ "github.com/coredns/coredns/plugin/rewrite"
_ "github.com/coredns/coredns/plugin/root"
_ "github.com/coredns/coredns/plugin/route53"
diff --git a/coremain/run.go b/coremain/run.go
index f8f4651e6..a948a4c9d 100644
--- a/coremain/run.go
+++ b/coremain/run.go
@@ -29,7 +29,7 @@ func init() {
flag.StringVar(&caddy.PidFile, "pidfile", "", "Path to write pid file")
flag.BoolVar(&version, "version", false, "Show version")
flag.BoolVar(&dnsserver.Quiet, "quiet", false, "Quiet mode (no initialization output)")
- flag.BoolVar(&logfile, "log", false, "Log to standard output")
+ flag.BoolVar(&logfile, "log", false, "Log to standard output") // noop for 1.1.4; drop in 1.2.0.
caddy.RegisterCaddyfileLoader("flag", caddy.LoaderFunc(confLoader))
caddy.SetDefaultCaddyfileLoader("default", caddy.LoaderFunc(defaultLoader))
@@ -62,10 +62,7 @@ func Run() {
mustLogFatal(fmt.Errorf("extra command line arguments: %s", flag.Args()))
}
- // Set up process log before anything bad happens
- if logfile {
- log.SetOutput(os.Stdout)
- }
+ log.SetOutput(os.Stdout)
log.SetFlags(log.LstdFlags)
if version {
diff --git a/plugin.cfg b/plugin.cfg
index 4f19395f5..c152684b7 100644
--- a/plugin.cfg
+++ b/plugin.cfg
@@ -38,7 +38,7 @@ cache:cache
rewrite:rewrite
dnssec:dnssec
autopath:autopath
-reverse:reverse
+reverse:deprecated
template:template
hosts:hosts
route53:route53
diff --git a/plugin/deprecated/setup.go b/plugin/deprecated/setup.go
index cf415c1ae..566e266ec 100644
--- a/plugin/deprecated/setup.go
+++ b/plugin/deprecated/setup.go
@@ -20,7 +20,7 @@ import (
)
// removed has the names of the plugins that need to error on startup.
-var removed = []string{"startup", "shutdown"}
+var removed = []string{"reverse"}
func setup(c *caddy.Controller) error {
c.Next()
diff --git a/plugin/proxy/README.md b/plugin/proxy/README.md
index 7341d31a6..f9dc3493b 100644
--- a/plugin/proxy/README.md
+++ b/plugin/proxy/README.md
@@ -31,7 +31,7 @@ proxy FROM TO... {
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]]
+ protocol [dns [force_tcp]|grpc [insecure|CACERT|KEY CERT|KEY CERT CACERT]]
}
~~~
@@ -54,8 +54,7 @@ proxy FROM TO... {
* `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
+ old DNS. The `grpc` option will talk to a server that has implemented
the [DnsService](https://github.com/coredns/coredns/blob/master/pb/dns.proto).
## Policies
@@ -73,10 +72,6 @@ available. This is to preeempt the case where the healthchecking (as a mechanism
## 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.
@@ -92,13 +87,6 @@ payload over HTTPS). Note that with `https_google` the entire transport is encry
* **KEY** **CERT** **CACERT** - Client authentication is used with the specified key/cert pair. The
server certificate is verified using the **CACERT** file.
-`https_google`
-: bootstrap **ADDRESS...** is used to (re-)resolve `dns.google.com`.
-
- 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`.
-
-
## Metrics
If monitoring is enabled (via the *prometheus* directive) then the following metric is exported:
@@ -108,7 +96,7 @@ If monitoring is enabled (via the *prometheus* directive) then the following met
* `coredns_proxy_request_count_total{server, proto, proto_proxy, family, to}` - query count per
upstream.
-Where `proxy_proto` is the protocol used (`dns`, `grpc`, or `https_google`) and `to` is **TO**
+Where `proxy_proto` is the protocol used (`dns` or `grpc`) and `to` is **TO**
specified in the config, `proto` is the protocol used by the incoming query ("tcp" or "udp"), family
the transport family ("1" for IPv4, and "2" for IPv6). `Server` is the server responsible for the
request (and metric). See the documention in the metrics plugin.
@@ -169,34 +157,3 @@ Proxy everything except `example.org` using the host's `resolv.conf`'s nameserve
}
}
~~~
-
-Proxy all requests within `example.org` to Google's `dns.google.com`.
-
-~~~ corefile
-. {
- 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`.
-
-~~~ corefile
-. {
- proxy . 1.2.3.4:53 {
- except example.org
- protocol https_google
- }
-}
-
-example.org {
- proxy . 8.8.8.8:53
-}
-~~~
-
-## Bugs
-
-When using the `google_https` protocol the health checking will health check the wrong endpoint.
-See <https://github.com/coredns/coredns/issues/1202> for some background.
diff --git a/plugin/proxy/dnstap_test.go b/plugin/proxy/dnstap_test.go
index 08cbd98a7..b07b081bc 100644
--- a/plugin/proxy/dnstap_test.go
+++ b/plugin/proxy/dnstap_test.go
@@ -47,7 +47,6 @@ func TestDnstap(t *testing.T) {
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) {
diff --git a/plugin/proxy/google.go b/plugin/proxy/google.go
deleted file mode 100644
index 0dbca6383..000000000
--- a/plugin/proxy/google.go
+++ /dev/null
@@ -1,219 +0,0 @@
-package proxy
-
-import (
- "context"
- "crypto/tls"
- "encoding/json"
- "fmt"
- "io/ioutil"
- "net"
- "net/http"
- "net/url"
- "time"
-
- "github.com/coredns/coredns/plugin/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 {
- // TODO(miek): Deprecate after 1.1.3 (that would be 1.2.0)
- log.Warning("https_google will be deprecated in the next release")
-
- 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.Warningf("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]
-
- new, err := g.bootstrapProxy.Lookup(state, g.endpoint, dns.TypeA)
- if err != nil {
- log.Warningf("Failed to bootstrap A records %q: %s", g.endpoint, err)
- } else {
- addrs, err1 := extractAnswer(new)
- if err1 != nil {
- log.Warningf("Failed to bootstrap A records %q: %s", g.endpoint, err1)
- } else {
-
- up := newUpstream(addrs, oldUpstream.(*staticUpstream))
- p.Upstreams = &[]Upstream{up}
-
- log.Infof("Bootstrapping A records %q found: %v", g.endpoint, addrs)
- }
- }
-
- go func() {
- tick := time.NewTicker(120 * time.Second)
-
- for {
- select {
- case <-tick.C:
-
- log.Infof("Resolving A records %q", g.endpoint)
-
- new, err := g.bootstrapProxy.Lookup(state, g.endpoint, dns.TypeA)
- if err != nil {
- log.Warningf("Failed to resolve A records %q: %s", g.endpoint, err)
- continue
- }
-
- addrs, err1 := extractAnswer(new)
- if err1 != nil {
- log.Warningf("Failed to resolve A records %q: %s", g.endpoint, err1)
- continue
- }
-
- up := newUpstream(addrs, oldUpstream.(*staticUpstream))
- p.Upstreams = &[]Upstream{up}
-
- log.Infof("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: 5 * time.Second,
- MaxFails: 3,
- },
- ex: old.ex,
- IgnoredSubDomains: old.IgnoredSubDomains,
- }
-
- 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: checkDownFunc(upstream),
- }
- upstream.Hosts[i] = uh
- }
- return upstream
-}
-
-const (
- // Default endpoint for this service.
- ghost = "dns.google.com."
-)
diff --git a/plugin/proxy/google_rr.go b/plugin/proxy/google_rr.go
deleted file mode 100644
index 3b9233b7b..000000000
--- a/plugin/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/plugin/proxy/google_test.go b/plugin/proxy/google_test.go
deleted file mode 100644
index 1ce591664..000000000
--- a/plugin/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/plugin/proxy/proxy.go b/plugin/proxy/proxy.go
index 5aeaa2a54..2a8e35096 100644
--- a/plugin/proxy/proxy.go
+++ b/plugin/proxy/proxy.go
@@ -129,14 +129,6 @@ func (p Proxy) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (
}
}
- // If protocol is https_google we do the health checks wrong, i.e. we're healthchecking the wrong
- // endpoint, hence the health check code below should not be executed. See issue #1202.
- // This is an ugly hack and the thing requires a rethink. Possibly in conjunction with moving
- // to the *forward* plugin.
- if upstream.Exchanger().Protocol() == "https_google" {
- continue
- }
-
timeout := host.FailTimeout
if timeout == 0 {
timeout = defaultFailTimeout
diff --git a/plugin/proxy/upstream.go b/plugin/proxy/upstream.go
index 421163b83..be3e0bdad 100644
--- a/plugin/proxy/upstream.go
+++ b/plugin/proxy/upstream.go
@@ -165,12 +165,7 @@ func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error {
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
+ // allow the config, but make noop
case "grpc":
if len(encArgs) == 2 && encArgs[1] == "insecure" {
u.ex = newGrpcClient(nil, u)
diff --git a/plugin/reverse/OWNERS b/plugin/reverse/OWNERS
deleted file mode 100644
index 2563b9f76..000000000
--- a/plugin/reverse/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-reviewers:
-approvers:
diff --git a/plugin/reverse/README.md b/plugin/reverse/README.md
deleted file mode 100644
index 7b4c0670f..000000000
--- a/plugin/reverse/README.md
+++ /dev/null
@@ -1,96 +0,0 @@
-# reverse
-
-## Name
-
-*reverse* - allows for dynamic responses to PTR and the related A/AAAA requests.
-
-## Description
-
-If a request matches a regular expression (see Template Syntax below) this plugin will generate a
-response. This is only done for "address" records (PTR, A and AAAA).
-
-## Syntax
-
-~~~
-reverse NETWORK... {
- hostname TEMPLATE
- [ttl TTL]
- [fallthrough [ZONES...]]
- [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 plugin.
- If **[ZONES...]** is omitted, then fallthrough happens for all zones for which the plugin
- is authoritative. If specific zones are listed (for example `in-addr.arpa` and `ip6.arpa`), then only
- queries for those zones will be subject to fallthrough.
-* `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
-
-~~~ corefile
-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
- }
-}
-~~~
-
-
-~~~ corefile
-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/plugin/reverse/network.go b/plugin/reverse/network.go
deleted file mode 100644
index 80d533382..000000000
--- a/plugin/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/plugin/reverse/network_test.go b/plugin/reverse/network_test.go
deleted file mode 100644
index a826707e5..000000000
--- a/plugin/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/plugin/reverse/reverse.go b/plugin/reverse/reverse.go
deleted file mode 100644
index 273e20551..000000000
--- a/plugin/reverse/reverse.go
+++ /dev/null
@@ -1,108 +0,0 @@
-package reverse
-
-import (
- "context"
- "net"
-
- "github.com/coredns/coredns/plugin"
- "github.com/coredns/coredns/plugin/pkg/dnsutil"
- "github.com/coredns/coredns/plugin/pkg/fall"
- "github.com/coredns/coredns/request"
- "github.com/miekg/dns"
-)
-
-// Reverse provides dynamic reverse DNS and the related forward RR.
-type Reverse struct {
- Next plugin.Handler
- Networks networks
-
- Fall fall.F
-}
-
-// ServeDNS implements the plugin.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 = 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.Fall.Through(state.Name()) {
- return plugin.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/plugin/reverse/reverse_test.go b/plugin/reverse/reverse_test.go
deleted file mode 100644
index 6d07ace29..000000000
--- a/plugin/reverse/reverse_test.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package reverse
-
-import (
- "context"
- "net"
- "regexp"
- "testing"
-
- "github.com/coredns/coredns/plugin"
- "github.com/coredns/coredns/plugin/pkg/dnstest"
- "github.com/coredns/coredns/plugin/test"
-
- "github.com/miekg/dns"
-)
-
-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,
- }},
- }
-
- tests := []struct {
- next plugin.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(tr.qname, tr.qtype)
-
- rec := dnstest.NewRecorder(&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/plugin/reverse/setup.go b/plugin/reverse/setup.go
deleted file mode 100644
index 1033a0de4..000000000
--- a/plugin/reverse/setup.go
+++ /dev/null
@@ -1,154 +0,0 @@
-package reverse
-
-import (
- "net"
- "regexp"
- "sort"
- "strconv"
- "strings"
-
- "github.com/coredns/coredns/core/dnsserver"
- "github.com/coredns/coredns/plugin"
- "github.com/coredns/coredns/plugin/pkg/fall"
- clog "github.com/coredns/coredns/plugin/pkg/log"
-
- "github.com/mholt/caddy"
-)
-
-var log = clog.NewWithPlugin("reverse")
-
-func init() {
- caddy.RegisterPlugin("reverse", caddy.Plugin{
- ServerType: "dns",
- Action: setup,
- })
-}
-
-func setup(c *caddy.Controller) error {
- networks, fall, err := reverseParse(c)
- if err != nil {
- return plugin.Error("reverse", err)
- }
-
- dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
- return Reverse{Next: next, Networks: networks, Fall: fall}
- })
-
- // TODO(miek): Deprecate after 1.1.3 (that would be 1.2.0)
- log.Warning("reverse will be deprecated in the next release")
-
- return nil
-}
-
-func reverseParse(c *caddy.Controller) (nets networks, f fall.F, 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] = plugin.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, f, c.Errf("network needs to be CIDR formatted: %q\n", cidr)
- }
- cidrs = append(cidrs, ipnet)
- }
- if len(cidrs) == 0 {
- return nil, f, 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, f, c.ArgErr()
- }
- template = c.Val()
-
- case "ttl":
- if !c.NextArg() {
- return nil, f, c.ArgErr()
- }
- ttl, err = strconv.Atoi(c.Val())
- if err != nil {
- return nil, f, err
- }
-
- case "wildcard":
- wildcard = true
-
- case "fallthrough":
- f.SetZonesFromArgs(c.RemainingArgs())
-
- default:
- return nil, f, 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, f, 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, f, 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, f, nil
-}
diff --git a/plugin/reverse/setup_test.go b/plugin/reverse/setup_test.go
deleted file mode 100644
index 3c88bc8e1..000000000
--- a/plugin/reverse/setup_test.go
+++ /dev/null
@@ -1,195 +0,0 @@
-package reverse
-
-import (
- "net"
- "reflect"
- "regexp"
- "testing"
-
- "github.com/mholt/caddy"
-)
-
-func TestSetup(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/test/reverse_test.go b/test/reverse_test.go
index 184a5fe1f..4ff74c737 100644
--- a/test/reverse_test.go
+++ b/test/reverse_test.go
@@ -12,83 +12,6 @@ import (
"github.com/miekg/dns"
)
-func TestReverseFallthrough(t *testing.T) {
- t.Parallel()
- name, rm, err := test.TempFile(".", exampleOrg)
- if err != nil {
- t.Fatalf("failed to create zone: %s", err)
- }
- defer rm()
-
- corefile := `arpa:0 example.org:0 {
- reverse 10.32.0.0/16 {
- hostname ip-{ip}.{zone[2]}
- #fallthrough
- }
- file ` + name + ` example.org
-}
-`
-
- i, udp, _, err := CoreDNSServerAndPorts(corefile)
- if err != nil {
- t.Fatalf("Could not get CoreDNS serving instance: %s", err)
- }
-
- log.SetOutput(ioutil.Discard)
-
- p := proxy.NewLookup([]string{udp})
- state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
- resp, err := p.Lookup(state, "example.org.", dns.TypeA)
- if err != nil {
- t.Fatal("Expected to receive reply, but didn't")
- }
- // Reply should be SERVFAIL because of no fallthrough
- if resp.Rcode != dns.RcodeServerFailure {
- t.Fatalf("Expected SERVFAIL, but got: %d", resp.Rcode)
- }
-
- // Stop the server.
- i.Stop()
-
- // And redo with fallthrough enabled
-
- corefile = `arpa:0 example.org:0 {
- reverse 10.32.0.0/16 {
- hostname ip-{ip}.{zone[2]}
- fallthrough
- }
- file ` + name + ` example.org
-}
-`
-
- i, err = CoreDNSServer(corefile)
- if err != nil {
- t.Fatalf("Could not get CoreDNS serving instance: %s", err)
- }
-
- udp, _ = CoreDNSServerPorts(i, 0)
- if udp == "" {
- t.Fatalf("Could not get UDP listening port")
- }
- defer i.Stop()
-
- p = proxy.NewLookup([]string{udp})
- resp, err = p.Lookup(state, "example.org.", dns.TypeA)
- if err != nil {
- t.Fatal("Expected to receive reply, but didn't")
- }
-
- if len(resp.Answer) == 0 {
- t.Error("Expected to at least one RR in the answer section, got none")
- }
- if resp.Answer[0].Header().Rrtype != dns.TypeA {
- t.Errorf("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype)
- }
- if resp.Answer[0].(*dns.A).A.String() != "127.0.0.1" {
- t.Errorf("Expected 127.0.0.1, got: %s", resp.Answer[0].(*dns.A).A.String())
- }
-}
-
func TestReverseCorefile(t *testing.T) {
corefile := `10.0.0.0/24:0 {
whoami