mirror of
https://github.com/coredns/coredns.git
synced 2025-11-12 23:12:17 -05:00
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
8
middleware/file/notify.go
Normal file
8
middleware/file/notify.go
Normal file
@@ -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
|
||||
|
||||
}
|
||||
21
middleware/file/tree/all.go
Normal file
21
middleware/file/tree/all.go
Normal file
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
61
middleware/file/xfr.go
Normal file
61
middleware/file/xfr.go
Normal file
@@ -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.
|
||||
34
middleware/file/xfr_test.go
Normal file
34
middleware/file/xfr_test.go
Normal file
@@ -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
|
||||
}
|
||||
@@ -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?
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user