mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-30 17:53:21 -04:00 
			
		
		
		
	Implement a DNS zone
Full implementation, DNS (and in the future DNSSEC). Returns answer in a hopefully standards compliant way. Testing with my miek.nl zone are included as well. This should correctly handle nodata, nxdomain and cnames.
This commit is contained in:
		| @@ -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 |  | ||||||
| } |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user