mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-31 10:13:14 -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 | ## Syntax | ||||||
|  |  | ||||||
| ~~~ | ~~~ | ||||||
| rewrite FROM TO | rewrite FIELD FROM TO | ||||||
| ~~~ | ~~~ | ||||||
|  |  | ||||||
|  | * **FIELD** is (`type`, `class`, `name`, ...) | ||||||
| * **FROM** is the exact name of type to match | * **FROM** is the exact name of type to match | ||||||
| * **TO** is the destination name or type to rewrite to | * **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; | 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 ANY HINFO`. | 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 | 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 CH IN`. | 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 | 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 miek.nl example.org`. | 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 | If you specify multiple rules and an incoming query matches on multiple (simple) rules, only | ||||||
| the first rewrite is applied. | the first rewrite is applied. | ||||||
|  |  | ||||||
|  |  | ||||||
| > Everything below this line has not been implemented, yet. | > 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 | package rewrite | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"strings" |  | ||||||
|  |  | ||||||
| 	"github.com/miekg/coredns/middleware" | 	"github.com/miekg/coredns/middleware" | ||||||
|  |  | ||||||
| 	"github.com/miekg/dns" | 	"github.com/miekg/dns" | ||||||
| 	"golang.org/x/net/context" | 	"golang.org/x/net/context" | ||||||
| ) | ) | ||||||
| @@ -59,64 +56,6 @@ func (rw Rewrite) Name() string { return "rewrite" } | |||||||
| type Rule interface { | type Rule interface { | ||||||
| 	// Rewrite rewrites the internal location of the current request. | 	// Rewrite rewrites the internal location of the current request. | ||||||
| 	Rewrite(*dns.Msg) Result | 	Rewrite(*dns.Msg) Result | ||||||
| } | 	// New returns a new rule. | ||||||
|  | 	New(...string) Rule | ||||||
| // 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 |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,9 +20,9 @@ func TestRewrite(t *testing.T) { | |||||||
| 	rw := Rewrite{ | 	rw := Rewrite{ | ||||||
| 		Next: middleware.HandlerFunc(msgPrinter), | 		Next: middleware.HandlerFunc(msgPrinter), | ||||||
| 		Rules: []Rule{ | 		Rules: []Rule{ | ||||||
| 			NewSimpleRule("from.nl.", "to.nl."), | 			Fields["name"].New("from.nl.", "to.nl."), | ||||||
| 			NewSimpleRule("CH", "IN"), | 			Fields["class"].New("CH", "IN"), | ||||||
| 			NewSimpleRule("ANY", "HINFO"), | 			Fields["type"].New("ANY", "HINFO"), | ||||||
| 		}, | 		}, | ||||||
| 		noRevert: true, | 		noRevert: true, | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| package rewrite | package rewrite | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"strings" | 	"log" | ||||||
|  |  | ||||||
| 	"github.com/miekg/coredns/core/dnsserver" | 	"github.com/miekg/coredns/core/dnsserver" | ||||||
| 	"github.com/miekg/coredns/middleware" | 	"github.com/miekg/coredns/middleware" | ||||||
| @@ -108,8 +108,12 @@ func rewriteParse(c *caddy.Controller) ([]Rule, error) { | |||||||
|  |  | ||||||
| 		// the only unhandled case is 2 and above | 		// the only unhandled case is 2 and above | ||||||
| 		default: | 		default: | ||||||
| 			rule = NewSimpleRule(args[0], strings.Join(args[1:], " ")) | 			if _, ok := Fields[args[0]]; ok { | ||||||
| 			simpleRules = append(simpleRules, rule) | 				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