aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/setup/file.go22
-rw-r--r--middleware/file/file.go11
-rw-r--r--middleware/file/file.md21
-rw-r--r--middleware/file/notify.go8
-rw-r--r--middleware/file/tree/all.go21
-rw-r--r--middleware/file/tree/tree.go2
-rw-r--r--middleware/file/xfr.go61
-rw-r--r--middleware/file/xfr_test.go34
-rw-r--r--middleware/file/zone.go38
-rw-r--r--middleware/recorder.go4
10 files changed, 195 insertions, 27 deletions
diff --git a/core/setup/file.go b/core/setup/file.go
index 858b784c2..7128b77aa 100644
--- a/core/setup/file.go
+++ b/core/setup/file.go
@@ -32,7 +32,6 @@ func fileParse(c *Controller) (file.Zones, error) {
origin := c.ServerBlockHosts[c.ServerBlockHostIndex]
if c.NextArg() {
- c.Next()
origin = c.Val()
}
// normalize this origin
@@ -42,12 +41,31 @@ func fileParse(c *Controller) (file.Zones, error) {
if err != nil {
return file.Zones{}, err
}
-
zone, err := file.Parse(reader, origin, fileName)
if err == nil {
z[origin] = zone
}
names = append(names, origin)
+ if c.NextBlock() {
+ what := c.Val()
+ if !c.NextArg() {
+ return file.Zones{}, c.ArgErr()
+ }
+ value := c.Val()
+ var err error
+ switch what {
+ case "transfer":
+ if value == "out" {
+ z[origin].Transfer.Out = true
+ }
+ if value == "in" {
+ z[origin].Transfer.In = true
+ }
+ }
+ if err != nil {
+ return file.Zones{}, err
+ }
+ }
}
}
return file.Zones{Z: z, Names: names}, nil
diff --git a/middleware/file/file.go b/middleware/file/file.go
index 5e4e0b357..237515a57 100644
--- a/middleware/file/file.go
+++ b/middleware/file/file.go
@@ -1,10 +1,5 @@
package file
-// TODO(miek): the zone's implementation is basically non-existent
-// we return a list and when searching for an answer we iterate
-// over the list. This must be moved to a tree-like structure and
-// have some fluff for DNSSEC (and be memory efficient).
-
import (
"io"
"log"
@@ -19,7 +14,6 @@ type (
File struct {
Next middleware.Handler
Zones Zones
- // Maybe a list of all zones as well, as a []string?
}
Zones struct {
@@ -40,6 +34,11 @@ func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
return f.Next.ServeDNS(ctx, w, r)
}
+ if state.Proto() != "udp" && state.QType() == dns.TypeAXFR {
+ xfr := Xfr{z}
+ return xfr.ServeDNS(ctx, w, r)
+ }
+
rrs, extra, result := z.Lookup(qname, state.QType(), state.Do())
m := new(dns.Msg)
diff --git a/middleware/file/file.md b/middleware/file/file.md
index 2e23b0332..bd9a24d1e 100644
--- a/middleware/file/file.md
+++ b/middleware/file/file.md
@@ -15,26 +15,15 @@ file dbfile [zones...]
* `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.
+If you want to round robin A and AAAA responses look at the `loadbalance` middleware.
~~~
-file {
- db <dsds>
- masters [...masters...]
+file dbfile [zones... ] {
+ transfer in|out
}
~~~
-
-
-
-
-* `path` /skydns
-* `endpoint` endpoints...
-* `stubzones`
+* `transfer` enable zone transfers, for now only `transfer out` does something. It enables outgoing
+ zone transfers when defined.
## Examples
-
-dnssec {
- file blaat, transparant allow already signed responses
- ksk bliep.dsdsk
-}
diff --git a/middleware/file/notify.go b/middleware/file/notify.go
new file mode 100644
index 000000000..cb61cc050
--- /dev/null
+++ b/middleware/file/notify.go
@@ -0,0 +1,8 @@
+package file
+
+// Notify sends notifies to the configured remotes. It will try up to three times
+// before giving up on a specific remote.
+func Notify(remotes []string) error {
+ return nil
+
+}
diff --git a/middleware/file/tree/all.go b/middleware/file/tree/all.go
new file mode 100644
index 000000000..f621e3465
--- /dev/null
+++ b/middleware/file/tree/all.go
@@ -0,0 +1,21 @@
+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
+}
diff --git a/middleware/file/tree/tree.go b/middleware/file/tree/tree.go
index db57c2092..234060eba 100644
--- a/middleware/file/tree/tree.go
+++ b/middleware/file/tree/tree.go
@@ -528,6 +528,8 @@ func (n *Node) floor(rr dns.RR) *Node {
return n
}
+// TODO(successor, predecessor)
+
// Ceil returns the smallest value equal to or greater than the query q according to q.Compare().
func (t *Tree) Ceil(rr dns.RR) *Elem {
if t.Root == nil {
diff --git a/middleware/file/xfr.go b/middleware/file/xfr.go
new file mode 100644
index 000000000..297de2fc5
--- /dev/null
+++ b/middleware/file/xfr.go
@@ -0,0 +1,61 @@
+package file
+
+import (
+ "fmt"
+
+ "github.com/miekg/coredns/middleware"
+
+ "github.com/miekg/dns"
+ "golang.org/x/net/context"
+)
+
+type (
+ Xfr struct {
+ *Zone
+ }
+)
+
+// Serve an AXFR (or maybe later an IXFR) as well.
+func (x Xfr) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
+ state := middleware.State{W: w, Req: r}
+ if !x.TransferAllowed(state) {
+ return dns.RcodeServerFailure, nil
+ }
+ if state.QType() != dns.TypeAXFR {
+ return 0, fmt.Errorf("file: xfr called with non xfr type: %d", state.QType())
+ }
+ if state.Proto() == "udp" {
+ return 0, fmt.Errorf("file: xfr called with udp")
+ }
+
+ 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])
+ 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
+}
+
+//const transferLength = 10e3 // Start a new envelop after message reaches this size.
+const transferLength = 100 // Start a new envelop after message reaches this size.
diff --git a/middleware/file/xfr_test.go b/middleware/file/xfr_test.go
new file mode 100644
index 000000000..07caaf1d9
--- /dev/null
+++ b/middleware/file/xfr_test.go
@@ -0,0 +1,34 @@
+package file
+
+import (
+ "fmt"
+ "strings"
+)
+
+func ExampleZone_All() {
+ zone, err := Parse(strings.NewReader(dbMiekNL), testzone, "stdin")
+ 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
index 57eb8d997..bac420669 100644
--- a/middleware/file/zone.go
+++ b/middleware/file/zone.go
@@ -1,20 +1,29 @@
package file
import (
+ "github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/file/tree"
"github.com/miekg/dns"
)
+type Transfer struct {
+ Out bool
+ In bool
+ // more later
+}
+
type Zone struct {
SOA *dns.SOA
- SIG []*dns.RRSIG
+ SIG []dns.RR
name string
*tree.Tree
+ Masters []string
+ Transfer *Transfer
}
func NewZone(name string) *Zone {
- return &Zone{name: dns.Fqdn(name), Tree: &tree.Tree{}}
+ return &Zone{name: dns.Fqdn(name), Tree: &tree.Tree{}, Transfer: &Transfer{}}
}
func (z *Zone) Insert(r dns.RR) {
@@ -24,3 +33,28 @@ func (z *Zone) Insert(r dns.RR) {
func (z *Zone) Delete(r dns.RR) {
z.Tree.Delete(r)
}
+
+// It the transfer request allowed.
+func (z *Zone) TransferAllowed(state middleware.State) bool {
+ if z.Transfer == nil {
+ return false
+ }
+ return z.Transfer.Out
+}
+
+// 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 {
+ records := []dns.RR{}
+ allNodes := z.Tree.All()
+ for _, a := range allNodes {
+ records = append(records, a.All()...)
+ }
+
+ if len(z.SIG) > 0 {
+ records = append(z.SIG, records...)
+ }
+ return append([]dns.RR{z.SOA}, records...)
+}
+
+// Apex function?
diff --git a/middleware/recorder.go b/middleware/recorder.go
index 19a15463e..6b926e48d 100644
--- a/middleware/recorder.go
+++ b/middleware/recorder.go
@@ -37,7 +37,9 @@ func NewResponseRecorder(w dns.ResponseWriter) *ResponseRecorder {
// underlying ResponseWriter's WriteMsg method.
func (r *ResponseRecorder) WriteMsg(res *dns.Msg) error {
r.rcode = res.Rcode
- r.size = res.Len()
+ // We may get called multiple times (axfr for instance).
+ // Save the last message, but add the sizes.
+ r.size += res.Len()
r.msg = res
return r.ResponseWriter.WriteMsg(res)
}