From fa1c90a479cce4666c32c4aae8728fc12f09980b Mon Sep 17 00:00:00 2001 From: Bob Wasniak Date: Tue, 7 Feb 2017 16:53:16 -0500 Subject: [PATCH] Add field keywords to rewrite middleware (#497) * Require Field for rewrite rules * review feedback changes * fix ut * fix typo, add warning message --- middleware/rewrite/README.md | 16 ++++---- middleware/rewrite/class.go | 31 ++++++++++++++ middleware/rewrite/field.go | 13 ++++++ middleware/rewrite/name.go | 29 +++++++++++++ middleware/rewrite/rewrite.go | 65 +----------------------------- middleware/rewrite/rewrite_test.go | 6 +-- middleware/rewrite/setup.go | 10 +++-- middleware/rewrite/type.go | 30 ++++++++++++++ 8 files changed, 124 insertions(+), 76 deletions(-) create mode 100644 middleware/rewrite/class.go create mode 100644 middleware/rewrite/field.go create mode 100644 middleware/rewrite/name.go create mode 100644 middleware/rewrite/type.go diff --git a/middleware/rewrite/README.md b/middleware/rewrite/README.md index 92118b955..ed5aa58e1 100644 --- a/middleware/rewrite/README.md +++ b/middleware/rewrite/README.md @@ -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. ~~~ diff --git a/middleware/rewrite/class.go b/middleware/rewrite/class.go new file mode 100644 index 000000000..78cca1a13 --- /dev/null +++ b/middleware/rewrite/class.go @@ -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 +} diff --git a/middleware/rewrite/field.go b/middleware/rewrite/field.go new file mode 100644 index 000000000..5ccad0d60 --- /dev/null +++ b/middleware/rewrite/field.go @@ -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{}, +} diff --git a/middleware/rewrite/name.go b/middleware/rewrite/name.go new file mode 100644 index 000000000..645e1dc04 --- /dev/null +++ b/middleware/rewrite/name.go @@ -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 +} diff --git a/middleware/rewrite/rewrite.go b/middleware/rewrite/rewrite.go index adbdbca15..cb5bbc288 100644 --- a/middleware/rewrite/rewrite.go +++ b/middleware/rewrite/rewrite.go @@ -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 } diff --git a/middleware/rewrite/rewrite_test.go b/middleware/rewrite/rewrite_test.go index 2dc658738..c9753635c 100644 --- a/middleware/rewrite/rewrite_test.go +++ b/middleware/rewrite/rewrite_test.go @@ -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, } diff --git a/middleware/rewrite/setup.go b/middleware/rewrite/setup.go index 9405fcd6a..81fbacfb9 100644 --- a/middleware/rewrite/setup.go +++ b/middleware/rewrite/setup.go @@ -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) + } } } diff --git a/middleware/rewrite/type.go b/middleware/rewrite/type.go new file mode 100644 index 000000000..1cbc882ec --- /dev/null +++ b/middleware/rewrite/type.go @@ -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 +}