commit
4be4e7430c
10 changed files with 983 additions and 86 deletions
|
@ -1,13 +1,10 @@
|
||||||
package setup
|
package setup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/miekg/coredns/middleware"
|
"github.com/miekg/coredns/middleware"
|
||||||
"github.com/miekg/coredns/middleware/file"
|
"github.com/miekg/coredns/middleware/file"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// File sets up the file middleware.
|
// File sets up the file middleware.
|
||||||
|
@ -23,8 +20,7 @@ func File(c *Controller) (middleware.Middleware, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileParse(c *Controller) (file.Zones, error) {
|
func fileParse(c *Controller) (file.Zones, error) {
|
||||||
// Maybe multiple, each for each zone.
|
z := make(map[string]*file.Zone)
|
||||||
z := make(map[string]file.Zone)
|
|
||||||
names := []string{}
|
names := []string{}
|
||||||
for c.Next() {
|
for c.Next() {
|
||||||
if c.Val() == "file" {
|
if c.Val() == "file" {
|
||||||
|
@ -42,7 +38,12 @@ func fileParse(c *Controller) (file.Zones, error) {
|
||||||
// normalize this origin
|
// normalize this origin
|
||||||
origin = middleware.Host(origin).StandardHost()
|
origin = middleware.Host(origin).StandardHost()
|
||||||
|
|
||||||
zone, err := parseZone(origin, fileName)
|
reader, err := os.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return file.Zones{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
zone, err := file.Parse(reader, origin, fileName)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
z[origin] = zone
|
z[origin] = zone
|
||||||
}
|
}
|
||||||
|
@ -51,24 +52,3 @@ func fileParse(c *Controller) (file.Zones, error) {
|
||||||
}
|
}
|
||||||
return file.Zones{Z: z, Names: names}, nil
|
return file.Zones{Z: z, Names: names}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// parsrZone parses the zone in filename and returns a []RR or an error.
|
|
||||||
func parseZone(origin, fileName string) (file.Zone, error) {
|
|
||||||
f, err := os.Open(fileName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tokens := dns.ParseZone(f, origin, fileName)
|
|
||||||
zone := make([]dns.RR, 0, defaultZoneSize)
|
|
||||||
for x := range tokens {
|
|
||||||
if x.Error != nil {
|
|
||||||
log.Printf("[ERROR] failed to parse %s: %v", origin, x.Error)
|
|
||||||
return nil, x.Error
|
|
||||||
}
|
|
||||||
zone = append(zone, x.RR)
|
|
||||||
}
|
|
||||||
return file.Zone(zone), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultZoneSize = 20 // A made up number.
|
|
||||||
|
|
|
@ -14,7 +14,8 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
|
||||||
return e.Next.ServeDNS(ctx, w, r)
|
return e.Next.ServeDNS(ctx, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
m := state.AnswerMessage()
|
m := new(dns.Msg)
|
||||||
|
m.SetReply(r)
|
||||||
m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
|
m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -6,12 +6,13 @@ package file
|
||||||
// have some fluff for DNSSEC (and be memory efficient).
|
// have some fluff for DNSSEC (and be memory efficient).
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"io"
|
||||||
|
"log"
|
||||||
"golang.org/x/net/context"
|
|
||||||
|
|
||||||
"github.com/miekg/coredns/middleware"
|
"github.com/miekg/coredns/middleware"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -21,9 +22,8 @@ type (
|
||||||
// Maybe a list of all zones as well, as a []string?
|
// Maybe a list of all zones as well, as a []string?
|
||||||
}
|
}
|
||||||
|
|
||||||
Zone []dns.RR
|
|
||||||
Zones struct {
|
Zones struct {
|
||||||
Z map[string]Zone // utterly braindead impl. TODO(miek): fix
|
Z map[string]*Zone
|
||||||
Names []string
|
Names []string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -35,57 +35,51 @@ func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
|
||||||
if zone == "" {
|
if zone == "" {
|
||||||
return f.Next.ServeDNS(ctx, w, r)
|
return f.Next.ServeDNS(ctx, w, r)
|
||||||
}
|
}
|
||||||
|
z, ok := f.Zones.Z[zone]
|
||||||
|
if !ok {
|
||||||
|
return f.Next.ServeDNS(ctx, w, r)
|
||||||
|
}
|
||||||
|
|
||||||
names, nodata := f.Zones.Z[zone].lookup(qname, state.QType())
|
rrs, extra, result := z.Lookup(qname, state.QType(), state.Do())
|
||||||
var answer *dns.Msg
|
|
||||||
switch {
|
m := new(dns.Msg)
|
||||||
case nodata:
|
m.SetReply(r)
|
||||||
answer = state.AnswerMessage()
|
m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
|
||||||
answer.Ns = names
|
|
||||||
case len(names) == 0:
|
switch result {
|
||||||
answer = state.AnswerMessage()
|
case Success:
|
||||||
answer.Ns = names
|
// case?
|
||||||
answer.Rcode = dns.RcodeNameError
|
m.Answer = rrs
|
||||||
case len(names) > 0:
|
m.Extra = extra
|
||||||
answer = state.AnswerMessage()
|
// Ns section
|
||||||
answer.Answer = names
|
case NameError:
|
||||||
|
m.Rcode = dns.RcodeNameError
|
||||||
|
fallthrough
|
||||||
|
case NoData:
|
||||||
|
// case?
|
||||||
|
m.Ns = rrs
|
||||||
default:
|
default:
|
||||||
answer = state.ErrorMessage(dns.RcodeServerFailure)
|
// TODO
|
||||||
}
|
}
|
||||||
// Check return size, etc. TODO(miek)
|
// sizing and Do bit RRSIG
|
||||||
w.WriteMsg(answer)
|
w.WriteMsg(m)
|
||||||
return 0, nil
|
return dns.RcodeSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lookup will try to find qname and qtype in z. It returns the
|
// Parse parses the zone in filename and returns a new Zone or an error.
|
||||||
// records found *or* a boolean saying NODATA. If the answer
|
func Parse(f io.Reader, origin, fileName string) (*Zone, error) {
|
||||||
// is NODATA then the RR returned is the SOA record.
|
tokens := dns.ParseZone(f, dns.Fqdn(origin), fileName)
|
||||||
//
|
z := NewZone(origin)
|
||||||
// TODO(miek): EXTREMELY STUPID IMPLEMENTATION.
|
for x := range tokens {
|
||||||
// Doesn't do much, no delegation, no cname, nothing really, etc.
|
if x.Error != nil {
|
||||||
// TODO(miek): even NODATA looks broken
|
log.Printf("[ERROR] failed to parse %s: %v", origin, x.Error)
|
||||||
func (z Zone) lookup(qname string, qtype uint16) ([]dns.RR, bool) {
|
return nil, x.Error
|
||||||
var (
|
|
||||||
nodata bool
|
|
||||||
rep []dns.RR
|
|
||||||
soa dns.RR
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, rr := range z {
|
|
||||||
if rr.Header().Rrtype == dns.TypeSOA {
|
|
||||||
soa = rr
|
|
||||||
}
|
}
|
||||||
// Match function in Go DNS?
|
if x.RR.Header().Rrtype == dns.TypeSOA {
|
||||||
if strings.ToLower(rr.Header().Name) == qname {
|
z.SOA = x.RR.(*dns.SOA)
|
||||||
if rr.Header().Rrtype == qtype {
|
continue
|
||||||
rep = append(rep, rr)
|
|
||||||
nodata = false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
z.Insert(x.RR)
|
||||||
}
|
}
|
||||||
if nodata {
|
return z, nil
|
||||||
return []dns.RR{soa}, true
|
|
||||||
}
|
|
||||||
return rep, false
|
|
||||||
}
|
}
|
||||||
|
|
40
middleware/file/file.md
Normal file
40
middleware/file/file.md
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# file
|
||||||
|
|
||||||
|
`file` enabled reading zone data from a RFC-1035 styled file.
|
||||||
|
|
||||||
|
The etcd middleware makes extensive use of the proxy middleware to forward and query
|
||||||
|
other servers in the network.
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
~~~
|
||||||
|
file dbfile [zones...]
|
||||||
|
~~~
|
||||||
|
|
||||||
|
* `dbfile` the database file to read and parse.
|
||||||
|
* `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.
|
||||||
|
|
||||||
|
~~~
|
||||||
|
file {
|
||||||
|
db <dsds>
|
||||||
|
masters [...masters...]
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* `path` /skydns
|
||||||
|
* `endpoint` endpoints...
|
||||||
|
* `stubzones`
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
dnssec {
|
||||||
|
file blaat, transparant allow already signed responses
|
||||||
|
ksk bliep.dsdsk
|
||||||
|
}
|
75
middleware/file/lookup.go
Normal file
75
middleware/file/lookup.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package file
|
||||||
|
|
||||||
|
import "github.com/miekg/dns"
|
||||||
|
|
||||||
|
// Result is the result of a Lookup
|
||||||
|
type Result int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Success Result = iota
|
||||||
|
NameError
|
||||||
|
NoData // aint no offical NoData return code.
|
||||||
|
)
|
||||||
|
|
||||||
|
// Lookup looks up qname and qtype in the zone, when do is true DNSSEC are included as well.
|
||||||
|
// Two sets of records are returned, one for the answer and one for the additional section.
|
||||||
|
func (z *Zone) Lookup(qname string, qtype uint16, do bool) ([]dns.RR, []dns.RR, Result) {
|
||||||
|
// TODO(miek): implement DNSSEC
|
||||||
|
var rr dns.RR
|
||||||
|
mk, known := dns.TypeToRR[qtype]
|
||||||
|
if !known {
|
||||||
|
return nil, nil, NameError
|
||||||
|
// Uhm...?
|
||||||
|
// rr = new(RFC3597)
|
||||||
|
} else {
|
||||||
|
rr = mk()
|
||||||
|
}
|
||||||
|
if qtype == dns.TypeSOA {
|
||||||
|
return z.lookupSOA(do)
|
||||||
|
}
|
||||||
|
|
||||||
|
rr.Header().Name = qname
|
||||||
|
elem := z.Tree.Get(rr)
|
||||||
|
if elem == nil {
|
||||||
|
return []dns.RR{z.SOA}, nil, NameError
|
||||||
|
}
|
||||||
|
rrs := elem.Types(dns.TypeCNAME)
|
||||||
|
if len(rrs) > 0 { // should only ever be 1 actually; TODO(miek) check for this?
|
||||||
|
// lookup target from the cname
|
||||||
|
rr.Header().Name = rrs[0].(*dns.CNAME).Target
|
||||||
|
elem := z.Tree.Get(rr)
|
||||||
|
if elem == nil {
|
||||||
|
return rrs, nil, Success
|
||||||
|
}
|
||||||
|
return rrs, elem.All(), Success
|
||||||
|
}
|
||||||
|
|
||||||
|
rrs = elem.Types(qtype)
|
||||||
|
if len(rrs) == 0 {
|
||||||
|
return []dns.RR{z.SOA}, nil, NoData
|
||||||
|
}
|
||||||
|
// Need to check sub-type on RRSIG records to only include the correctly
|
||||||
|
// typed ones.
|
||||||
|
return rrs, nil, Success
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Zone) lookupSOA(do bool) ([]dns.RR, []dns.RR, Result) {
|
||||||
|
return []dns.RR{z.SOA}, nil, Success
|
||||||
|
}
|
||||||
|
|
||||||
|
// signatureForSubType range through the signature and return the correct
|
||||||
|
// ones for the subtype.
|
||||||
|
func (z *Zone) signatureForSubType(rrs []dns.RR, subtype uint16, do bool) []dns.RR {
|
||||||
|
if !do {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
sigs := []dns.RR{}
|
||||||
|
for _, sig := range rrs {
|
||||||
|
if s, ok := sig.(*dns.RRSIG); ok {
|
||||||
|
if s.TypeCovered == subtype {
|
||||||
|
sigs = append(sigs, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sigs
|
||||||
|
}
|
174
middleware/file/lookup_test.go
Normal file
174
middleware/file/lookup_test.go
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/miekg/coredns/middleware"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dnsTestCases = []dnsTestCase{
|
||||||
|
{
|
||||||
|
Qname: "miek.nl.", Qtype: dns.TypeSOA,
|
||||||
|
Answer: []dns.RR{
|
||||||
|
newSOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Qname: "miek.nl.", Qtype: dns.TypeAAAA,
|
||||||
|
Answer: []dns.RR{
|
||||||
|
newAAAA("miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Qname: "miek.nl.", Qtype: dns.TypeMX,
|
||||||
|
Answer: []dns.RR{
|
||||||
|
newMX("miek.nl. 1800 IN MX 1 aspmx.l.google.com."),
|
||||||
|
newMX("miek.nl. 1800 IN MX 10 aspmx2.googlemail.com."),
|
||||||
|
newMX("miek.nl. 1800 IN MX 10 aspmx3.googlemail.com."),
|
||||||
|
newMX("miek.nl. 1800 IN MX 5 alt1.aspmx.l.google.com."),
|
||||||
|
newMX("miek.nl. 1800 IN MX 5 alt2.aspmx.l.google.com."),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Qname: "www.miek.nl.", Qtype: dns.TypeA,
|
||||||
|
Answer: []dns.RR{
|
||||||
|
newCNAME("www.miek.nl. 1800 IN CNAME a.miek.nl."),
|
||||||
|
},
|
||||||
|
|
||||||
|
Extra: []dns.RR{
|
||||||
|
newA("a.miek.nl. 1800 IN A 139.162.196.78"),
|
||||||
|
newAAAA("a.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Qname: "a.miek.nl.", Qtype: dns.TypeSRV,
|
||||||
|
Ns: []dns.RR{
|
||||||
|
newSOA("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{
|
||||||
|
newSOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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() }
|
||||||
|
|
||||||
|
const testzone = "miek.nl."
|
||||||
|
|
||||||
|
func TestLookup(t *testing.T) {
|
||||||
|
zone, err := Parse(strings.NewReader(dbMiekNL), testzone, "stdin")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expect no error when reading zone, got %q", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fm := File{Next: handler(), Zones: Zones{Z: map[string]*Zone{testzone: zone}, Names: []string{testzone}}}
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
for _, tc := range dnsTestCases {
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.SetQuestion(dns.Fqdn(tc.Qname), tc.Qtype)
|
||||||
|
|
||||||
|
rec := middleware.NewResponseRecorder(&middleware.TestResponseWriter{})
|
||||||
|
_, err := fm.ServeDNS(ctx, rec, m)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected no error, got %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp := rec.Msg()
|
||||||
|
|
||||||
|
sort.Sort(rrSet(resp.Answer))
|
||||||
|
sort.Sort(rrSet(resp.Ns))
|
||||||
|
sort.Sort(rrSet(resp.Extra))
|
||||||
|
|
||||||
|
if resp.Rcode != tc.Rcode {
|
||||||
|
t.Errorf("rcode is %q, expected %q", dns.RcodeToString[resp.Rcode], dns.RcodeToString[tc.Rcode])
|
||||||
|
t.Logf("%v\n", resp)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
t.Logf("%v\n", resp)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
t.Logf("%v\n", resp)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
t.Logf("%v\n", resp)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type dnsTestCase struct {
|
||||||
|
Qname string
|
||||||
|
Qtype uint16
|
||||||
|
Rcode int
|
||||||
|
Answer []dns.RR
|
||||||
|
Ns []dns.RR
|
||||||
|
Extra []dns.RR
|
||||||
|
}
|
||||||
|
|
||||||
|
func newA(rr string) *dns.A { r, _ := dns.NewRR(rr); return r.(*dns.A) }
|
||||||
|
func newAAAA(rr string) *dns.AAAA { r, _ := dns.NewRR(rr); return r.(*dns.AAAA) }
|
||||||
|
func newCNAME(rr string) *dns.CNAME { r, _ := dns.NewRR(rr); return r.(*dns.CNAME) }
|
||||||
|
func newSRV(rr string) *dns.SRV { r, _ := dns.NewRR(rr); return r.(*dns.SRV) }
|
||||||
|
func newSOA(rr string) *dns.SOA { r, _ := dns.NewRR(rr); return r.(*dns.SOA) }
|
||||||
|
func newNS(rr string) *dns.NS { r, _ := dns.NewRR(rr); return r.(*dns.NS) }
|
||||||
|
func newPTR(rr string) *dns.PTR { r, _ := dns.NewRR(rr); return r.(*dns.PTR) }
|
||||||
|
func newTXT(rr string) *dns.TXT { r, _ := dns.NewRR(rr); return r.(*dns.TXT) }
|
||||||
|
func newMX(rr string) *dns.MX { r, _ := dns.NewRR(rr); return r.(*dns.MX) }
|
||||||
|
|
||||||
|
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`
|
||||||
|
|
||||||
|
func handler() middleware.Handler {
|
||||||
|
return middleware.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
|
||||||
|
})
|
||||||
|
}
|
585
middleware/file/tree/tree.go
Normal file
585
middleware/file/tree/tree.go
Normal file
|
@ -0,0 +1,585 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// TODO(miek): locking? lockfree
|
||||||
|
// TODO(miek): fix docs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Elem struct {
|
||||||
|
m map[uint16][]dns.RR
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 types from with type qtype from e.
|
||||||
|
func (e *Elem) Types(qtype uint16) []dns.RR {
|
||||||
|
if rrs, ok := e.m[qtype]; ok {
|
||||||
|
// TODO(miek): length should never be zero here.
|
||||||
|
return rrs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Elem) All() []dns.RR {
|
||||||
|
list := []dns.RR{}
|
||||||
|
for _, rrs := range e.m {
|
||||||
|
list = append(list, rrs...)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
t := rr.Header().Rrtype
|
||||||
|
if e.m == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(miek): need case ignore compare that is more efficient.
|
||||||
|
func Less(a *Elem, rr dns.RR) int {
|
||||||
|
aname := ""
|
||||||
|
for _, ar := range a.m {
|
||||||
|
aname = strings.ToLower(ar[0].Header().Name)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
rname := strings.ToLower(rr.Header().Name)
|
||||||
|
if aname == rname {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if aname < rname {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the first match of q in the Tree. If insertion without
|
||||||
|
// replacement is used, this is probably not what you want.
|
||||||
|
func (t *Tree) Get(rr dns.RR) *Elem {
|
||||||
|
if t.Root == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n := t.Root.search(rr)
|
||||||
|
if n == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return n.Elem
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) search(rr dns.RR) *Node {
|
||||||
|
for n != nil {
|
||||||
|
switch c := Less(n.Elem, rr); {
|
||||||
|
case c == 0:
|
||||||
|
return n
|
||||||
|
case c < 0:
|
||||||
|
n = n.Left
|
||||||
|
default:
|
||||||
|
n = n.Right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert inserts the Comparable e into the Tree at the first match found
|
||||||
|
// with e or when a nil node is reached. Insertion without replacement can
|
||||||
|
// specified by ensuring that e.Compare() never returns 0. If insert without
|
||||||
|
// replacement is performed, a distinct query Comparable must be used that
|
||||||
|
// can return 0 with a Compare() call.
|
||||||
|
func (t *Tree) Insert(rr dns.RR) {
|
||||||
|
var d int
|
||||||
|
t.Root, d = t.Root.insert(rr)
|
||||||
|
t.Count += d
|
||||||
|
t.Root.Color = Black
|
||||||
|
}
|
||||||
|
|
||||||
|
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); {
|
||||||
|
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. If insertion without
|
||||||
|
// replacement has been used, the left-most minimum will be deleted.
|
||||||
|
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. If insertion without
|
||||||
|
// replacement has been used, the right-most maximum will be deleted.
|
||||||
|
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 return with DeleteNode.
|
||||||
|
func (t *Tree) Delete(rr dns.RR) {
|
||||||
|
if t.Root == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// If there is an element, remove the rr from it
|
||||||
|
el := t.Get(rr)
|
||||||
|
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 e according to Compare(). Note that Compare must
|
||||||
|
// identify the target node uniquely and in cases where non-unique keys are used,
|
||||||
|
// attributes used to break ties must be used to determine tree ordering during insertion.
|
||||||
|
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) < 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) == 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) == 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the minimum value stored in the tree. This will be the left-most minimum value if
|
||||||
|
// insertion without replacement has been used.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the maximum value stored in the tree. This will be the right-most maximum value if
|
||||||
|
// insertion without replacement has been used.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Floor returns the greatest value equal to or less than the query q according to q.Compare().
|
||||||
|
func (t *Tree) Floor(rr dns.RR) *Elem {
|
||||||
|
if t.Root == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n := t.Root.floor(rr)
|
||||||
|
if n == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return n.Elem
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) floor(rr dns.RR) *Node {
|
||||||
|
if n == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch c := Less(n.Elem, rr); {
|
||||||
|
case c == 0:
|
||||||
|
return n
|
||||||
|
case c < 0:
|
||||||
|
return n.Left.floor(rr)
|
||||||
|
default:
|
||||||
|
if r := n.Right.floor(rr); r != nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n := t.Root.ceil(rr)
|
||||||
|
if n == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return n.Elem
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) ceil(rr dns.RR) *Node {
|
||||||
|
if n == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch c := Less(n.Elem, rr); {
|
||||||
|
case c == 0:
|
||||||
|
return n
|
||||||
|
case c > 0:
|
||||||
|
return n.Right.ceil(rr)
|
||||||
|
default:
|
||||||
|
if l := n.Left.ceil(rr); 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.
|
||||||
|
*/
|
26
middleware/file/zone.go
Normal file
26
middleware/file/zone.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/miekg/coredns/middleware/file/tree"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Zone struct {
|
||||||
|
SOA *dns.SOA
|
||||||
|
SIG []*dns.RRSIG
|
||||||
|
name string
|
||||||
|
*tree.Tree
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewZone(name string) *Zone {
|
||||||
|
return &Zone{name: dns.Fqdn(name), Tree: &tree.Tree{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Zone) Insert(r dns.RR) {
|
||||||
|
z.Tree.Insert(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Zone) Delete(r dns.RR) {
|
||||||
|
z.Tree.Delete(r)
|
||||||
|
}
|
30
middleware/file/zone_test.go
Normal file
30
middleware/file/zone_test.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestZoneInsert(t *testing.T) {
|
||||||
|
z := NewZone("miek.nl")
|
||||||
|
rr, _ := dns.NewRR("miek.nl. IN A 127.0.0.1")
|
||||||
|
z.Insert(rr)
|
||||||
|
|
||||||
|
t.Logf("%+v\n", z)
|
||||||
|
|
||||||
|
elem := z.Get(rr)
|
||||||
|
t.Logf("%+v\n", elem)
|
||||||
|
if elem != nil {
|
||||||
|
t.Logf("%+v\n", elem.Types(dns.TypeA))
|
||||||
|
}
|
||||||
|
z.Delete(rr)
|
||||||
|
|
||||||
|
t.Logf("%+v\n", z)
|
||||||
|
|
||||||
|
elem = z.Get(rr)
|
||||||
|
t.Logf("%+v\n", elem)
|
||||||
|
if elem != nil {
|
||||||
|
t.Logf("%+v\n", elem.Types(dns.TypeA))
|
||||||
|
}
|
||||||
|
}
|
|
@ -179,11 +179,3 @@ func (s State) ErrorMessage(rcode int) *dns.Msg {
|
||||||
m.SetRcode(s.Req, rcode)
|
m.SetRcode(s.Req, rcode)
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
// AnswerMessage returns an error message suitable for sending
|
|
||||||
// back to the client.
|
|
||||||
func (s State) AnswerMessage() *dns.Msg {
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.SetReply(s.Req)
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue