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:
parent
b8e75509cc
commit
fa1c90a479
8 changed files with 124 additions and 76 deletions
|
@ -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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue