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:
Bob Wasniak 2017-02-07 16:53:16 -05:00 committed by Miek Gieben
parent b8e75509cc
commit fa1c90a479
8 changed files with 124 additions and 76 deletions

View file

@ -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.
~~~

View 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
}

View 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{},
}

View 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
}

View file

@ -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
}

View file

@ -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,
}

View file

@ -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)
}
}
}

View 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
}