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
|
## 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
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue