mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-30 17:53:21 -04:00 
			
		
		
		
	Add field keywords to rewrite middleware (#497)
* Require Field for rewrite rules * review feedback changes * fix ut * fix typo, add warning message
This commit is contained in:
		| @@ -7,24 +7,26 @@ accommodate most dynamic back-end applications. | ||||
| ## Syntax | ||||
|  | ||||
| ~~~ | ||||
| rewrite FROM TO | ||||
| rewrite FIELD FROM TO | ||||
| ~~~ | ||||
|  | ||||
| * **FIELD** is (`type`, `class`, `name`, ...) | ||||
| * **FROM** is the exact name of type to match | ||||
| * **TO** is the destination name or type to rewrite to | ||||
|  | ||||
| If from *and* to look like a DNS type (`A`, `MX`, etc.), the type of the message will be rewriten; | ||||
| e.g., to rewrite ANY queries to HINFO, use `rewrite ANY HINFO`. | ||||
| When the FIELD is `type` and FROM is (`A`, `MX`, etc.), the type of the message will be rewritten; | ||||
| e.g., to rewrite ANY queries to HINFO, use `rewrite type ANY HINFO`. | ||||
|  | ||||
| If from *and* to look like a DNS class (`IN`, `CH`, or `HS`) the class of the message will be | ||||
| rewritten; e.g., to rewrite CH queries to IN use `rewrite CH IN`. | ||||
| When the FIELD is `class` and FROM is (`IN`, `CH`, or `HS`) the class of the message will be | ||||
| rewritten; e.g., to rewrite CH queries to IN use `rewrite class CH IN`. | ||||
|  | ||||
| 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, e.g., `rewrite miek.nl example.org`. | ||||
| When the FIELD is `name` the query name in the message is rewritten; this | ||||
| needs to be a full match of the name, e.g., `rewrite name miek.nl example.org`. | ||||
|  | ||||
| If you specify multiple rules and an incoming query matches on multiple (simple) rules, only | ||||
| the first rewrite is applied. | ||||
|  | ||||
|  | ||||
| > Everything below this line has not been implemented, yet. | ||||
|  | ||||
| ~~~ | ||||
|   | ||||
							
								
								
									
										31
									
								
								middleware/rewrite/class.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								middleware/rewrite/class.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| // Package rewrite is middleware for rewriting requests internally to something different. | ||||
| package rewrite | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/miekg/dns" | ||||
| ) | ||||
|  | ||||
| // ClassRule is a class rewrite rule. | ||||
| type ClassRule struct { | ||||
| 	fromClass, toClass uint16 | ||||
| } | ||||
|  | ||||
| // Initializer | ||||
| func (rule ClassRule) New(args ...string) Rule { | ||||
| 	from, to := args[0], strings.Join(args[1:], " ") | ||||
| 	return &ClassRule{dns.StringToClass[from], dns.StringToClass[to]} | ||||
|  | ||||
| } | ||||
|  | ||||
| // Rewrite rewrites the the current request. | ||||
| func (rule ClassRule) Rewrite(r *dns.Msg) Result { | ||||
| 	if rule.fromClass > 0 && rule.toClass > 0 { | ||||
| 		if r.Question[0].Qclass == rule.fromClass { | ||||
| 			r.Question[0].Qclass = rule.toClass | ||||
| 			return RewriteDone | ||||
| 		} | ||||
| 	} | ||||
| 	return RewriteIgnored | ||||
| } | ||||
							
								
								
									
										13
									
								
								middleware/rewrite/field.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								middleware/rewrite/field.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| // Package rewrite is middleware for rewriting requests internally to something different. | ||||
| package rewrite | ||||
|  | ||||
| /* | ||||
| Additional FIELD keywords may be implemented to support more rewrite use-cases. | ||||
| New Rule types must be added to the Fields map. | ||||
| The type must implement `New` and `Rewrite` functions. | ||||
| */ | ||||
| var Fields = map[string]Rule{ | ||||
| 	"name":  NameRule{}, | ||||
| 	"type":  TypeRule{}, | ||||
| 	"class": ClassRule{}, | ||||
| } | ||||
							
								
								
									
										29
									
								
								middleware/rewrite/name.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								middleware/rewrite/name.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| // Package rewrite is middleware for rewriting requests internally to something different. | ||||
| package rewrite | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/miekg/coredns/middleware" | ||||
| 	"github.com/miekg/dns" | ||||
| ) | ||||
|  | ||||
| // NameRule is a name rewrite rule. | ||||
| type NameRule struct { | ||||
| 	From, To string | ||||
| } | ||||
|  | ||||
| // Initializer | ||||
| func (rule NameRule) New(args ...string) Rule { | ||||
| 	from, to := args[0], strings.Join(args[1:], " ") | ||||
| 	return &NameRule{middleware.Name(from).Normalize(), middleware.Name(to).Normalize()} | ||||
| } | ||||
|  | ||||
| // Rewrite rewrites the the current request. | ||||
| func (rule NameRule) Rewrite(r *dns.Msg) Result { | ||||
| 	if rule.From == r.Question[0].Name { | ||||
| 		r.Question[0].Name = rule.To | ||||
| 		return RewriteDone | ||||
| 	} | ||||
| 	return RewriteIgnored | ||||
| } | ||||
| @@ -2,10 +2,7 @@ | ||||
| package rewrite | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/miekg/coredns/middleware" | ||||
|  | ||||
| 	"github.com/miekg/dns" | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
| @@ -59,64 +56,6 @@ func (rw Rewrite) Name() string { return "rewrite" } | ||||
| type Rule interface { | ||||
| 	// Rewrite rewrites the internal location of the current request. | ||||
| 	Rewrite(*dns.Msg) Result | ||||
| } | ||||
|  | ||||
| // SimpleRule is a simple rewrite rule. If the From and To look like a type | ||||
| // the type of the request is rewritten, otherwise the name is. | ||||
| // Note: TSIG signed requests will be invalid. | ||||
| type SimpleRule struct { | ||||
| 	From, To           string | ||||
| 	fromType, toType   uint16 | ||||
| 	fromClass, toClass uint16 | ||||
| } | ||||
|  | ||||
| // NewSimpleRule creates a new Simple Rule | ||||
| func NewSimpleRule(from, to string) SimpleRule { | ||||
| 	tpf := dns.StringToType[from] | ||||
| 	tpt := dns.StringToType[to] | ||||
|  | ||||
| 	// ANY is both a type and class, ANY class rewritting is way more less frequent | ||||
| 	// so we default to ANY as a type. | ||||
| 	clf := dns.StringToClass[from] | ||||
| 	clt := dns.StringToClass[to] | ||||
| 	if from == "ANY" { | ||||
| 		clf = 0 | ||||
| 		clt = 0 | ||||
| 	} | ||||
|  | ||||
| 	// It's only a type/class if uppercase is used. | ||||
| 	if from != strings.ToUpper(from) { | ||||
| 		tpf = 0 | ||||
| 		clf = 0 | ||||
| 		from = middleware.Name(from).Normalize() | ||||
| 	} | ||||
| 	if to != strings.ToUpper(to) { | ||||
| 		tpt = 0 | ||||
| 		clt = 0 | ||||
| 		to = middleware.Name(to).Normalize() | ||||
| 	} | ||||
| 	return SimpleRule{From: from, To: to, fromType: tpf, toType: tpt, fromClass: clf, toClass: clt} | ||||
| } | ||||
|  | ||||
| // Rewrite rewrites the the current request. | ||||
| func (s SimpleRule) Rewrite(r *dns.Msg) Result { | ||||
| 	if s.fromType > 0 && s.toType > 0 { | ||||
| 		if r.Question[0].Qtype == s.fromType { | ||||
| 			r.Question[0].Qtype = s.toType | ||||
| 			return RewriteDone | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if s.fromClass > 0 && s.toClass > 0 { | ||||
| 		if r.Question[0].Qclass == s.fromClass { | ||||
| 			r.Question[0].Qclass = s.toClass | ||||
| 			return RewriteDone | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if s.From == r.Question[0].Name { | ||||
| 		r.Question[0].Name = s.To | ||||
| 		return RewriteDone | ||||
| 	} | ||||
| 	return RewriteIgnored | ||||
| 	// New returns a new rule. | ||||
| 	New(...string) Rule | ||||
| } | ||||
|   | ||||
| @@ -20,9 +20,9 @@ func TestRewrite(t *testing.T) { | ||||
| 	rw := Rewrite{ | ||||
| 		Next: middleware.HandlerFunc(msgPrinter), | ||||
| 		Rules: []Rule{ | ||||
| 			NewSimpleRule("from.nl.", "to.nl."), | ||||
| 			NewSimpleRule("CH", "IN"), | ||||
| 			NewSimpleRule("ANY", "HINFO"), | ||||
| 			Fields["name"].New("from.nl.", "to.nl."), | ||||
| 			Fields["class"].New("CH", "IN"), | ||||
| 			Fields["type"].New("ANY", "HINFO"), | ||||
| 		}, | ||||
| 		noRevert: true, | ||||
| 	} | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package rewrite | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
| 	"log" | ||||
|  | ||||
| 	"github.com/miekg/coredns/core/dnsserver" | ||||
| 	"github.com/miekg/coredns/middleware" | ||||
| @@ -108,8 +108,12 @@ func rewriteParse(c *caddy.Controller) ([]Rule, error) { | ||||
|  | ||||
| 		// the only unhandled case is 2 and above | ||||
| 		default: | ||||
| 			rule = NewSimpleRule(args[0], strings.Join(args[1:], " ")) | ||||
| 			simpleRules = append(simpleRules, rule) | ||||
| 			if _, ok := Fields[args[0]]; ok { | ||||
| 				rule = Fields[args[0]].New(args[1:]...) | ||||
| 				simpleRules = append(simpleRules, rule) | ||||
| 			} else { | ||||
| 				log.Printf("[WARN] %s is not a valid field, ignore %s", args[0], args) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										30
									
								
								middleware/rewrite/type.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								middleware/rewrite/type.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| // Package rewrite is middleware for rewriting requests internally to something different. | ||||
| package rewrite | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/miekg/dns" | ||||
| ) | ||||
|  | ||||
| // TypeRule is a type rewrite rule. | ||||
| type TypeRule struct { | ||||
| 	fromType, toType uint16 | ||||
| } | ||||
|  | ||||
| // Initializer | ||||
| func (rule TypeRule) New(args ...string) Rule { | ||||
| 	from, to := args[0], strings.Join(args[1:], " ") | ||||
| 	return &TypeRule{dns.StringToType[from], dns.StringToType[to]} | ||||
| } | ||||
|  | ||||
| // Rewrite rewrites the the current request. | ||||
| func (rule TypeRule) Rewrite(r *dns.Msg) Result { | ||||
| 	if rule.fromType > 0 && rule.toType > 0 { | ||||
| 		if r.Question[0].Qtype == rule.fromType { | ||||
| 			r.Question[0].Qtype = rule.toType | ||||
| 			return RewriteDone | ||||
| 		} | ||||
| 	} | ||||
| 	return RewriteIgnored | ||||
| } | ||||
		Reference in New Issue
	
	Block a user