mirror of
				https://github.com/coredns/coredns.git
				synced 2025-11-04 03:03:14 -05: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 {
 | 
				
			||||||
 | 
									rule = Fields[args[0]].New(args[1:]...)
 | 
				
			||||||
				simpleRules = append(simpleRules, rule)
 | 
									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