mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-27 08:14:18 -04:00 
			
		
		
		
	fixed
This commit is contained in:
		| @@ -53,13 +53,13 @@ var directiveOrder = []directive{ | ||||
| 	// Directives that inject handlers (middleware) | ||||
| 	{"prometheus", setup.Prometheus}, | ||||
| 	{"rewrite", setup.Rewrite}, | ||||
| 	{"file", setup.File}, | ||||
| 	{"loadbalance", setup.Loadbalance}, | ||||
| 	{"log", setup.Log}, | ||||
| 	{"errors", setup.Errors}, | ||||
|  | ||||
| 	{"etcd", setup.Etcd}, | ||||
| 	{"file", setup.File}, | ||||
| 	{"reflect", setup.Reflect}, | ||||
|  | ||||
| 	{"proxy", setup.Proxy}, | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										19
									
								
								core/setup/loadbalance.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								core/setup/loadbalance.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| package setup | ||||
|  | ||||
| import ( | ||||
| 	"github.com/miekg/coredns/middleware" | ||||
| 	"github.com/miekg/coredns/middleware/loadbalance" | ||||
| ) | ||||
|  | ||||
| // Root sets up the root file path of the server. | ||||
| func Loadbalance(c *Controller) (middleware.Middleware, error) { | ||||
| 	for c.Next() { | ||||
| 		// and choosing the correct balancer | ||||
| 		// TODO(miek): block and option parsing | ||||
| 	} | ||||
| 	return func(next middleware.Handler) middleware.Handler { | ||||
| 		return loadbalance.RoundRobin{Next: next} | ||||
| 	}, nil | ||||
|  | ||||
| 	return nil, nil | ||||
| } | ||||
| @@ -1,28 +0,0 @@ | ||||
| package setup | ||||
|  | ||||
| import ( | ||||
| 	"github.com/miekg/coredns/middleware" | ||||
| 	"github.com/miekg/coredns/middleware/reflect" | ||||
| ) | ||||
|  | ||||
| // Reflect sets up the reflect middleware. | ||||
| func Reflect(c *Controller) (middleware.Middleware, error) { | ||||
| 	if err := reflectParse(c); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return func(next middleware.Handler) middleware.Handler { | ||||
| 		return reflect.Reflect{Next: next} | ||||
| 	}, nil | ||||
|  | ||||
| } | ||||
|  | ||||
| func reflectParse(c *Controller) error { | ||||
| 	for c.Next() { | ||||
| 		if c.Val() == "reflect" { | ||||
| 			if c.NextArg() { | ||||
| 				return c.ArgErr() | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										19
									
								
								middleware/loadbalance/handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								middleware/loadbalance/handler.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| // Package loadbalance is middleware for rewriting responses to do "load balancing" | ||||
| package loadbalance | ||||
|  | ||||
| import ( | ||||
| 	"github.com/miekg/coredns/middleware" | ||||
| 	"github.com/miekg/dns" | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
|  | ||||
| // RoundRobin is middleware to rewrite responses for "load balancing". | ||||
| type RoundRobin struct { | ||||
| 	Next middleware.Handler | ||||
| } | ||||
|  | ||||
| // ServeHTTP implements the middleware.Handler interface. | ||||
| func (rr RoundRobin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { | ||||
| 	wrr := NewRoundRobinResponseWriter(w) | ||||
| 	return rr.Next.ServeDNS(ctx, wrr, r) | ||||
| } | ||||
							
								
								
									
										68
									
								
								middleware/loadbalance/loadbalance.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								middleware/loadbalance/loadbalance.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| package loadbalance | ||||
|  | ||||
| import "github.com/miekg/dns" | ||||
|  | ||||
| type RoundRobinResponseWriter struct { | ||||
| 	dns.ResponseWriter | ||||
| } | ||||
|  | ||||
| func NewRoundRobinResponseWriter(w dns.ResponseWriter) *RoundRobinResponseWriter { | ||||
| 	return &RoundRobinResponseWriter{w} | ||||
| } | ||||
|  | ||||
| func (r *RoundRobinResponseWriter) WriteMsg(res *dns.Msg) error { | ||||
| 	if res.Rcode != dns.RcodeSuccess { | ||||
| 		return r.ResponseWriter.WriteMsg(res) | ||||
| 	} | ||||
| 	if len(res.Answer) == 1 { | ||||
| 		return r.ResponseWriter.WriteMsg(res) | ||||
| 	} | ||||
|  | ||||
| 	// put CNAMEs first, randomize a/aaaa's and put packet back together. | ||||
| 	// TODO(miek): check family and give v6 more prio? | ||||
| 	cname := []dns.RR{} | ||||
| 	address := []dns.RR{} | ||||
| 	rest := []dns.RR{} | ||||
| 	for _, r := range res.Answer { | ||||
| 		switch r.Header().Rrtype { | ||||
| 		case dns.TypeCNAME: | ||||
| 			cname = append(cname, r) | ||||
| 		case dns.TypeA, dns.TypeAAAA: | ||||
| 			address = append(address, r) | ||||
| 		default: | ||||
| 			rest = append(rest, r) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	switch l := len(address); l { | ||||
| 	case 0, 1: | ||||
| 		return r.ResponseWriter.WriteMsg(res) | ||||
| 	case 2: | ||||
| 		if dns.Id()%2 == 0 { | ||||
| 			address[0], address[1] = address[1], address[0] | ||||
| 		} | ||||
| 	default: | ||||
| 		for j := 0; j < l*(int(dns.Id())%4+1); j++ { | ||||
| 			q := int(dns.Id()) % l | ||||
| 			p := int(dns.Id()) % l | ||||
| 			if q == p { | ||||
| 				p = (p + 1) % l | ||||
| 			} | ||||
| 			address[q], address[p] = address[p], address[q] | ||||
| 		} | ||||
| 	} | ||||
| 	res.Answer = append(cname, rest...) | ||||
| 	res.Answer = append(res.Answer, address...) | ||||
| 	return r.ResponseWriter.WriteMsg(res) | ||||
| } | ||||
|  | ||||
| func (r *RoundRobinResponseWriter) Write(buf []byte) (int, error) { | ||||
| 	// pack and unpack? Not likely | ||||
| 	n, err := r.ResponseWriter.Write(buf) | ||||
| 	return n, err | ||||
| } | ||||
|  | ||||
| func (r *RoundRobinResponseWriter) Hijack() { | ||||
| 	r.ResponseWriter.Hijack() | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										19
									
								
								middleware/loadbalance/loadbalance.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								middleware/loadbalance/loadbalance.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| # loadbalance | ||||
|  | ||||
| `loadbalance` acts as a round-robin DNS loadbalancer by randomizing A and AAAA records in the | ||||
| message. See [Wikipedia](https://en.wikipedia.org/wiki/Round-robin_DNS) about the pros and cons | ||||
| on this setup. | ||||
|  | ||||
| ## Syntax | ||||
|  | ||||
| ~~~ | ||||
| loadbalance [policy] | ||||
| ~~~ | ||||
|  | ||||
| * policy is how to balance, the default is "round_robin" | ||||
|  | ||||
| ## Examples | ||||
|  | ||||
| ~~~ | ||||
| loadbalance round_robin | ||||
| ~~~ | ||||
| @@ -1,6 +1,10 @@ | ||||
| package middleware | ||||
|  | ||||
| import "strings" | ||||
| import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/miekg/dns" | ||||
| ) | ||||
|  | ||||
| // Name represents a domain name. | ||||
| type Name string | ||||
| @@ -13,3 +17,8 @@ type Name string | ||||
| func (n Name) Matches(other string) bool { | ||||
| 	return strings.HasSuffix(string(n), other) | ||||
| } | ||||
|  | ||||
| // Normalize lowercases and makes n fully qualified. | ||||
| func (n Name) Normalize() string { | ||||
| 	return strings.ToLower(dns.Fqdn(string(n))) | ||||
| } | ||||
|   | ||||
| @@ -1,86 +0,0 @@ | ||||
| // Reflect provides middleware that reflects back some client properties. | ||||
| // This is the default middleware when Caddy is run without configuration. | ||||
| // | ||||
| // The left-most label must be `who`. | ||||
| // When queried for type A (resp. AAAA), it sends back the IPv4 (resp. v6) address. | ||||
| // In the additional section the port number and transport are shown. | ||||
| // Basic use pattern: | ||||
| // | ||||
| //	dig @localhost -p 1053 who.miek.nl A | ||||
| // | ||||
| //	;; ANSWER SECTION: | ||||
| //	who.miek.nl.		0	IN	A	127.0.0.1 | ||||
| // | ||||
| //	;; ADDITIONAL SECTION: | ||||
| //	who.miek.nl.		0	IN	TXT	"Port: 56195 (udp)" | ||||
| package reflect | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"net" | ||||
| 	"strings" | ||||
|  | ||||
| 	"golang.org/x/net/context" | ||||
|  | ||||
| 	"github.com/miekg/coredns/middleware" | ||||
| 	"github.com/miekg/dns" | ||||
| ) | ||||
|  | ||||
| type Reflect struct { | ||||
| 	Next middleware.Handler | ||||
| } | ||||
|  | ||||
| func (rl Reflect) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { | ||||
| 	state := middleware.State{Req: r, W: w} | ||||
|  | ||||
| 	class := r.Question[0].Qclass | ||||
| 	qname := r.Question[0].Name | ||||
| 	i, ok := dns.NextLabel(qname, 0) | ||||
|  | ||||
| 	if strings.ToLower(qname[:i]) != who || ok { | ||||
| 		err := state.ErrorMessage(dns.RcodeFormatError) | ||||
| 		w.WriteMsg(err) | ||||
| 		return dns.RcodeFormatError, errors.New(dns.RcodeToString[dns.RcodeFormatError]) | ||||
| 	} | ||||
|  | ||||
| 	answer := new(dns.Msg) | ||||
| 	answer.SetReply(r) | ||||
| 	answer.Compress = true | ||||
| 	answer.Authoritative = true | ||||
|  | ||||
| 	ip := state.IP() | ||||
| 	proto := state.Proto() | ||||
| 	port, _ := state.Port() | ||||
| 	family := state.Family() | ||||
| 	var rr dns.RR | ||||
|  | ||||
| 	switch family { | ||||
| 	case 1: | ||||
| 		rr = new(dns.A) | ||||
| 		rr.(*dns.A).Hdr = dns.RR_Header{Name: qname, Rrtype: dns.TypeA, Class: class, Ttl: 0} | ||||
| 		rr.(*dns.A).A = net.ParseIP(ip).To4() | ||||
| 	case 2: | ||||
| 		rr = new(dns.AAAA) | ||||
| 		rr.(*dns.AAAA).Hdr = dns.RR_Header{Name: qname, Rrtype: dns.TypeAAAA, Class: class, Ttl: 0} | ||||
| 		rr.(*dns.AAAA).AAAA = net.ParseIP(ip) | ||||
| 	} | ||||
|  | ||||
| 	t := new(dns.TXT) | ||||
| 	t.Hdr = dns.RR_Header{Name: qname, Rrtype: dns.TypeTXT, Class: class, Ttl: 0} | ||||
| 	t.Txt = []string{"Port: " + port + " (" + proto + ")"} | ||||
|  | ||||
| 	switch state.Type() { | ||||
| 	case "TXT": | ||||
| 		answer.Answer = append(answer.Answer, t) | ||||
| 		answer.Extra = append(answer.Extra, rr) | ||||
| 	default: | ||||
| 		fallthrough | ||||
| 	case "AAAA", "A": | ||||
| 		answer.Answer = append(answer.Answer, rr) | ||||
| 		answer.Extra = append(answer.Extra, t) | ||||
| 	} | ||||
| 	w.WriteMsg(answer) | ||||
| 	return 0, nil | ||||
| } | ||||
|  | ||||
| const who = "who." | ||||
| @@ -1 +0,0 @@ | ||||
| package reflect | ||||
| @@ -69,27 +69,31 @@ func NewSimpleRule(from, to string) SimpleRule { | ||||
| 	// It's only a type if uppercase is used. | ||||
| 	if from != strings.ToUpper(from) { | ||||
| 		tpf = 0 | ||||
| 		from = middleware.Name(from).Normalize() | ||||
| 	} | ||||
| 	if to != strings.ToUpper(to) { | ||||
| 		tpt = 0 | ||||
| 		to = middleware.Name(to).Normalize() | ||||
| 	} | ||||
|  | ||||
| 	// lowercase and fully qualify the others here? TODO(miek) | ||||
| 	return SimpleRule{From: from, To: to, fromType: tpf, toType: tpt} | ||||
| } | ||||
|  | ||||
| // Rewrite rewrites the the current request. | ||||
| func (s SimpleRule) Rewrite(r *dns.Msg) Result { | ||||
| 	// type rewrite | ||||
| 	if s.fromType > 0 && s.toType > 0 { | ||||
| 		if r.Question[0].Qtype == s.fromType { | ||||
| 			r.Question[0].Qtype = s.toType | ||||
| 			return RewriteDone | ||||
| 		} | ||||
|  | ||||
| 		return RewriteIgnored | ||||
| 	} | ||||
|  | ||||
| 	// if the question name matches the full name, or subset rewrite that | ||||
| 	// s.Question[0].Name | ||||
| 	// name rewite | ||||
| 	if s.From == r.Question[0].Name { | ||||
| 		r.Question[0].Name = s.To | ||||
| 		return RewriteDone | ||||
| 	} | ||||
| 	return RewriteIgnored | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -16,9 +16,13 @@ rewrite from to | ||||
| If from *and* to look like a DNS type (`A`, `MX`, etc.) the type of the message will be rewriten, | ||||
| i.e. to rewrite ANY queries to HINFO, use `rewrite ANY HINFO`. | ||||
|  | ||||
| If it does not look like a type a name is assumed and the qname in the message is rewritten. | ||||
| If it does not look like a type a name is assumed and the qname in the message is rewritten, this | ||||
| needs to be a full match of the name `rewrite miek.nl example.org`. | ||||
|  | ||||
| Advanced users may open a block and make a complex rewrite rule: | ||||
| TODO(miek): this has not yet been implemented. | ||||
|  | ||||
| > Everything below this line has not been implemented, yet. | ||||
|  | ||||
| ~~~ | ||||
| rewrite [basename] { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user