aboutsummaryrefslogtreecommitdiff
path: root/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'plugin')
-rw-r--r--plugin/rewrite/README.md9
-rw-r--r--plugin/rewrite/class.go5
-rw-r--r--plugin/rewrite/edns0.go46
-rw-r--r--plugin/rewrite/name.go5
-rw-r--r--plugin/rewrite/rewrite.go50
-rw-r--r--plugin/rewrite/rewrite_test.go47
-rw-r--r--plugin/rewrite/type.go5
7 files changed, 139 insertions, 28 deletions
diff --git a/plugin/rewrite/README.md b/plugin/rewrite/README.md
index 63334d09c..747e58e11 100644
--- a/plugin/rewrite/README.md
+++ b/plugin/rewrite/README.md
@@ -8,7 +8,7 @@ Rewrites are invisible to the client. There are simple rewrites (fast) and compl
## Syntax
~~~
-rewrite FIELD FROM TO
+rewrite [continue|stop] FIELD FROM TO
~~~
* **FIELD** is (`type`, `class`, `name`, ...)
@@ -26,8 +26,11 @@ needs to be a full match of the name, e.g., `rewrite name miek.nl example.org`.
When the FIELD is `edns0` an EDNS0 option can be appended to the request as described below.
-If you specify multiple rules and an incoming query matches on multiple (simple) rules, only
-the first rewrite is applied.
+If you specify multiple rules and an incoming query matches on multiple rules, the rewrite
+will behave as following
+* `continue` will continue apply the next rule in the rule list.
+* `stop` will consider the current rule is the last rule and will not continue. Default behaviour
+for not specifying this rule processing mode is `stop`
## EDNS0 Options
diff --git a/plugin/rewrite/class.go b/plugin/rewrite/class.go
index 8cc7d26b7..8befdf8c2 100644
--- a/plugin/rewrite/class.go
+++ b/plugin/rewrite/class.go
@@ -33,3 +33,8 @@ func (rule *classRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result {
}
return RewriteIgnored
}
+
+// Mode returns the processing mode
+func (rule *classRule) Mode() string {
+ return Stop
+}
diff --git a/plugin/rewrite/edns0.go b/plugin/rewrite/edns0.go
index d8b6f4128..65ce3b7f6 100644
--- a/plugin/rewrite/edns0.go
+++ b/plugin/rewrite/edns0.go
@@ -15,6 +15,7 @@ import (
// edns0LocalRule is a rewrite rule for EDNS0_LOCAL options
type edns0LocalRule struct {
+ mode string
action string
code uint16
data []byte
@@ -22,6 +23,7 @@ type edns0LocalRule struct {
// edns0VariableRule is a rewrite rule for EDNS0_LOCAL options with variable
type edns0VariableRule struct {
+ mode string
action string
code uint16
variable string
@@ -29,6 +31,7 @@ type edns0VariableRule struct {
// ends0NsidRule is a rewrite rule for EDNS0_NSID options
type edns0NsidRule struct {
+ mode string
action string
}
@@ -70,6 +73,11 @@ Option:
return result
}
+// Mode returns the processing mode
+func (rule *edns0NsidRule) Mode() string {
+ return rule.mode
+}
+
// Rewrite will alter the request EDNS0 local options
func (rule *edns0LocalRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result {
result := RewriteIgnored
@@ -102,8 +110,13 @@ func (rule *edns0LocalRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result {
return result
}
+// Mode returns the processing mode
+func (rule *edns0LocalRule) Mode() string {
+ return rule.mode
+}
+
// newEdns0Rule creates an EDNS0 rule of the appropriate type based on the args
-func newEdns0Rule(args ...string) (Rule, error) {
+func newEdns0Rule(mode string, args ...string) (Rule, error) {
if len(args) < 2 {
return nil, fmt.Errorf("too few arguments for an EDNS0 rule")
}
@@ -125,25 +138,25 @@ func newEdns0Rule(args ...string) (Rule, error) {
}
//Check for variable option
if strings.HasPrefix(args[3], "{") && strings.HasSuffix(args[3], "}") {
- return newEdns0VariableRule(action, args[2], args[3])
+ return newEdns0VariableRule(mode, action, args[2], args[3])
}
- return newEdns0LocalRule(action, args[2], args[3])
+ return newEdns0LocalRule(mode, action, args[2], args[3])
case "nsid":
if len(args) != 2 {
return nil, fmt.Errorf("EDNS0 NSID rules do not accept args")
}
- return &edns0NsidRule{action: action}, nil
+ return &edns0NsidRule{mode: mode, action: action}, nil
case "subnet":
if len(args) != 4 {
return nil, fmt.Errorf("EDNS0 subnet rules require exactly three args")
}
- return newEdns0SubnetRule(action, args[2], args[3])
+ return newEdns0SubnetRule(mode, action, args[2], args[3])
default:
return nil, fmt.Errorf("invalid rule type %q", ruleType)
}
}
-func newEdns0LocalRule(action, code, data string) (*edns0LocalRule, error) {
+func newEdns0LocalRule(mode, action, code, data string) (*edns0LocalRule, error) {
c, err := strconv.ParseUint(code, 0, 16)
if err != nil {
return nil, err
@@ -156,11 +169,11 @@ func newEdns0LocalRule(action, code, data string) (*edns0LocalRule, error) {
return nil, err
}
}
- return &edns0LocalRule{action: action, code: uint16(c), data: decoded}, nil
+ return &edns0LocalRule{mode: mode, action: action, code: uint16(c), data: decoded}, nil
}
// newEdns0VariableRule creates an EDNS0 rule that handles variable substitution
-func newEdns0VariableRule(action, code, variable string) (*edns0VariableRule, error) {
+func newEdns0VariableRule(mode, action, code, variable string) (*edns0VariableRule, error) {
c, err := strconv.ParseUint(code, 0, 16)
if err != nil {
return nil, err
@@ -169,7 +182,7 @@ func newEdns0VariableRule(action, code, variable string) (*edns0VariableRule, er
if !isValidVariable(variable) {
return nil, fmt.Errorf("unsupported variable name %q", variable)
}
- return &edns0VariableRule{action: action, code: uint16(c), variable: variable}, nil
+ return &edns0VariableRule{mode: mode, action: action, code: uint16(c), variable: variable}, nil
}
// ipToWire writes IP address to wire/binary format, 4 or 16 bytes depends on IPV4 or IPV6.
@@ -294,6 +307,11 @@ func (rule *edns0VariableRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result
return result
}
+// Mode returns the processing mode
+func (rule *edns0VariableRule) Mode() string {
+ return rule.mode
+}
+
func isValidVariable(variable string) bool {
switch variable {
case
@@ -311,12 +329,13 @@ func isValidVariable(variable string) bool {
// ends0SubnetRule is a rewrite rule for EDNS0 subnet options
type edns0SubnetRule struct {
+ mode string
v4BitMaskLen uint8
v6BitMaskLen uint8
action string
}
-func newEdns0SubnetRule(action, v4BitMaskLen, v6BitMaskLen string) (*edns0SubnetRule, error) {
+func newEdns0SubnetRule(mode, action, v4BitMaskLen, v6BitMaskLen string) (*edns0SubnetRule, error) {
v4Len, err := strconv.ParseUint(v4BitMaskLen, 0, 16)
if err != nil {
return nil, err
@@ -335,7 +354,7 @@ func newEdns0SubnetRule(action, v4BitMaskLen, v6BitMaskLen string) (*edns0Subnet
return nil, fmt.Errorf("invalid IPv6 bit mask length %d", v6Len)
}
- return &edns0SubnetRule{action: action,
+ return &edns0SubnetRule{mode: mode, action: action,
v4BitMaskLen: uint8(v4Len), v6BitMaskLen: uint8(v6Len)}, nil
}
@@ -400,6 +419,11 @@ func (rule *edns0SubnetRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result {
return result
}
+// Mode returns the processing mode
+func (rule *edns0SubnetRule) Mode() string {
+ return rule.mode
+}
+
// These are all defined actions.
const (
Replace = "replace"
diff --git a/plugin/rewrite/name.go b/plugin/rewrite/name.go
index 189133542..016ae4deb 100644
--- a/plugin/rewrite/name.go
+++ b/plugin/rewrite/name.go
@@ -22,3 +22,8 @@ func (rule *nameRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result {
}
return RewriteIgnored
}
+
+// Mode returns the processing mode
+func (rule *nameRule) Mode() string {
+ return Stop
+}
diff --git a/plugin/rewrite/rewrite.go b/plugin/rewrite/rewrite.go
index d4931445c..384d1718d 100644
--- a/plugin/rewrite/rewrite.go
+++ b/plugin/rewrite/rewrite.go
@@ -24,6 +24,14 @@ const (
RewriteStatus
)
+// These are defined processing mode.
+const (
+ // Processing should stop after completing this rule
+ Stop = "stop"
+ // Processing should continue to next rule
+ Continue = "continue"
+)
+
// Rewrite is plugin to rewrite requests internally before being handled.
type Rewrite struct {
Next plugin.Handler
@@ -37,10 +45,12 @@ func (rw Rewrite) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg
for _, rule := range rw.Rules {
switch result := rule.Rewrite(w, r); result {
case RewriteDone:
- if rw.noRevert {
- return plugin.NextOrFailure(rw.Name(), rw.Next, ctx, w, r)
+ if rule.Mode() == Stop {
+ if rw.noRevert {
+ return plugin.NextOrFailure(rw.Name(), rw.Next, ctx, w, r)
+ }
+ return plugin.NextOrFailure(rw.Name(), rw.Next, ctx, wr, r)
}
- return plugin.NextOrFailure(rw.Name(), rw.Next, ctx, wr, r)
case RewriteIgnored:
break
case RewriteStatus:
@@ -60,6 +70,8 @@ func (rw Rewrite) Name() string { return "rewrite" }
type Rule interface {
// Rewrite rewrites the current request.
Rewrite(dns.ResponseWriter, *dns.Msg) Result
+ // Mode returns the processing mode stop or continue
+ Mode() string
}
func newRule(args ...string) (Rule, error) {
@@ -67,19 +79,39 @@ func newRule(args ...string) (Rule, error) {
return nil, fmt.Errorf("no rule type specified for rewrite")
}
- ruleType := strings.ToLower(args[0])
- if ruleType != "edns0" && len(args) != 3 {
+ arg0 := strings.ToLower(args[0])
+ var ruleType string
+ var expectNumArgs, startArg int
+ mode := Stop
+ switch arg0 {
+ case Continue:
+ mode = arg0
+ ruleType = strings.ToLower(args[1])
+ expectNumArgs = len(args) - 1
+ startArg = 2
+ case Stop:
+ ruleType = strings.ToLower(args[1])
+ expectNumArgs = len(args) - 1
+ startArg = 2
+ default:
+ // for backward compability
+ ruleType = arg0
+ expectNumArgs = len(args)
+ startArg = 1
+ }
+
+ if ruleType != "edns0" && expectNumArgs != 3 {
return nil, fmt.Errorf("%s rules must have exactly two arguments", ruleType)
}
switch ruleType {
case "name":
- return newNameRule(args[1], args[2])
+ return newNameRule(args[startArg], args[startArg+1])
case "class":
- return newClassRule(args[1], args[2])
+ return newClassRule(args[startArg], args[startArg+1])
case "type":
- return newTypeRule(args[1], args[2])
+ return newTypeRule(args[startArg], args[startArg+1])
case "edns0":
- return newEdns0Rule(args[1:]...)
+ return newEdns0Rule(mode, args[startArg:]...)
default:
return nil, fmt.Errorf("invalid rule type %q", args[0])
}
diff --git a/plugin/rewrite/rewrite_test.go b/plugin/rewrite/rewrite_test.go
index 74a8594df..9ce87e517 100644
--- a/plugin/rewrite/rewrite_test.go
+++ b/plugin/rewrite/rewrite_test.go
@@ -88,6 +88,43 @@ func TestNewRule(t *testing.T) {
{[]string{"edns0", "subnet", "set", "24", "56"}, false, reflect.TypeOf(&edns0SubnetRule{})},
{[]string{"edns0", "subnet", "append", "24", "56"}, false, reflect.TypeOf(&edns0SubnetRule{})},
{[]string{"edns0", "subnet", "replace", "24", "56"}, false, reflect.TypeOf(&edns0SubnetRule{})},
+ {[]string{"unknown-action", "name", "a.com", "b.com"}, true, nil},
+ {[]string{"stop", "name", "a.com", "b.com"}, false, reflect.TypeOf(&nameRule{})},
+ {[]string{"continue", "name", "a.com", "b.com"}, false, reflect.TypeOf(&nameRule{})},
+ {[]string{"unknown-action", "type", "any", "a"}, true, nil},
+ {[]string{"stop", "type", "any", "a"}, false, reflect.TypeOf(&typeRule{})},
+ {[]string{"continue", "type", "any", "a"}, false, reflect.TypeOf(&typeRule{})},
+ {[]string{"unknown-action", "class", "ch", "in"}, true, nil},
+ {[]string{"stop", "class", "ch", "in"}, false, reflect.TypeOf(&classRule{})},
+ {[]string{"continue", "class", "ch", "in"}, false, reflect.TypeOf(&classRule{})},
+ {[]string{"unknown-action", "edns0", "local", "set", "0xffee", "abcedef"}, true, nil},
+ {[]string{"stop", "edns0", "local", "set", "0xffee", "abcdefg"}, false, reflect.TypeOf(&edns0LocalRule{})},
+ {[]string{"continue", "edns0", "local", "set", "0xffee", "abcdefg"}, false, reflect.TypeOf(&edns0LocalRule{})},
+ {[]string{"unknown-action", "edns0", "nsid", "set"}, true, nil},
+ {[]string{"stop", "edns0", "nsid", "set"}, false, reflect.TypeOf(&edns0NsidRule{})},
+ {[]string{"continue", "edns0", "nsid", "set"}, false, reflect.TypeOf(&edns0NsidRule{})},
+ {[]string{"unknown-action", "edns0", "local", "set", "0xffee", "{qname}"}, true, nil},
+ {[]string{"stop", "edns0", "local", "set", "0xffee", "{qname}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"stop", "edns0", "local", "set", "0xffee", "{qtype}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"stop", "edns0", "local", "set", "0xffee", "{client_ip}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"stop", "edns0", "local", "set", "0xffee", "{client_port}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"stop", "edns0", "local", "set", "0xffee", "{protocol}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"stop", "edns0", "local", "set", "0xffee", "{server_ip}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"stop", "edns0", "local", "set", "0xffee", "{server_port}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"continue", "edns0", "local", "set", "0xffee", "{qname}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"continue", "edns0", "local", "set", "0xffee", "{qtype}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"continue", "edns0", "local", "set", "0xffee", "{client_ip}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"continue", "edns0", "local", "set", "0xffee", "{client_port}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"continue", "edns0", "local", "set", "0xffee", "{protocol}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"continue", "edns0", "local", "set", "0xffee", "{server_ip}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"continue", "edns0", "local", "set", "0xffee", "{server_port}"}, false, reflect.TypeOf(&edns0VariableRule{})},
+ {[]string{"unknown-action", "edns0", "subnet", "set", "24", "64"}, true, nil},
+ {[]string{"stop", "edns0", "subnet", "set", "24", "56"}, false, reflect.TypeOf(&edns0SubnetRule{})},
+ {[]string{"stop", "edns0", "subnet", "append", "24", "56"}, false, reflect.TypeOf(&edns0SubnetRule{})},
+ {[]string{"stop", "edns0", "subnet", "replace", "24", "56"}, false, reflect.TypeOf(&edns0SubnetRule{})},
+ {[]string{"continue", "edns0", "subnet", "set", "24", "56"}, false, reflect.TypeOf(&edns0SubnetRule{})},
+ {[]string{"continue", "edns0", "subnet", "append", "24", "56"}, false, reflect.TypeOf(&edns0SubnetRule{})},
+ {[]string{"continue", "edns0", "subnet", "replace", "24", "56"}, false, reflect.TypeOf(&edns0SubnetRule{})},
}
for i, tc := range tests {
@@ -208,7 +245,7 @@ func TestRewriteEDNS0Local(t *testing.T) {
m.SetQuestion("example.com.", dns.TypeA)
m.Question[0].Qclass = dns.ClassINET
- r, err := newEdns0Rule(tc.args...)
+ r, err := newEdns0Rule("stop", tc.args...)
if err != nil {
t.Errorf("Error creating test rule: %s", err)
continue
@@ -232,9 +269,9 @@ func TestRewriteEDNS0Local(t *testing.T) {
func TestEdns0LocalMultiRule(t *testing.T) {
rules := []Rule{}
- r, _ := newEdns0Rule("local", "replace", "0xffee", "abcdef")
+ r, _ := newEdns0Rule("stop", "local", "replace", "0xffee", "abcdef")
rules = append(rules, r)
- r, _ = newEdns0Rule("local", "set", "0xffee", "fedcba")
+ r, _ = newEdns0Rule("stop", "local", "set", "0xffee", "fedcba")
rules = append(rules, r)
rw := Rewrite{
@@ -399,7 +436,7 @@ func TestRewriteEDNS0LocalVariable(t *testing.T) {
m.SetQuestion("example.com.", dns.TypeA)
m.Question[0].Qclass = dns.ClassINET
- r, err := newEdns0Rule(tc.args...)
+ r, err := newEdns0Rule("stop", tc.args...)
if err != nil {
t.Errorf("Error creating test rule: %s", err)
continue
@@ -510,7 +547,7 @@ func TestRewriteEDNS0Subnet(t *testing.T) {
m.SetQuestion("example.com.", dns.TypeA)
m.Question[0].Qclass = dns.ClassINET
- r, err := newEdns0Rule(tc.args...)
+ r, err := newEdns0Rule("stop", tc.args...)
if err != nil {
t.Errorf("Error creating test rule: %s", err)
continue
diff --git a/plugin/rewrite/type.go b/plugin/rewrite/type.go
index ae3efcc5a..a02d08603 100644
--- a/plugin/rewrite/type.go
+++ b/plugin/rewrite/type.go
@@ -35,3 +35,8 @@ func (rule *typeRule) Rewrite(w dns.ResponseWriter, r *dns.Msg) Result {
}
return RewriteIgnored
}
+
+// Mode returns the processing mode
+func (rule *typeRule) Mode() string {
+ return Stop
+}