aboutsummaryrefslogtreecommitdiff
path: root/plugin
diff options
context:
space:
mode:
authorGravatar slick-nic <nic@njcolledge.net> 2021-02-23 09:12:40 +0000
committerGravatar GitHub <noreply@github.com> 2021-02-23 10:12:40 +0100
commit01039312635d2577ec08f6f426fa395f0ccca392 (patch)
treeadd615081f63ba971230c8d86e517c1d108a08c0 /plugin
parentfe2b5f630d969bac5eda7b72e28f740e65778469 (diff)
downloadcoredns-01039312635d2577ec08f6f426fa395f0ccca392.tar.gz
coredns-01039312635d2577ec08f6f426fa395f0ccca392.tar.zst
coredns-01039312635d2577ec08f6f426fa395f0ccca392.zip
Rewrite SRV targets and additional names in response (#4287)
* Rewrite plugin - rewrite SRV targets and names in response answer and additional records Signed-off-by: Nic Colledge <nic@njcolledge.net> * Added README content to describe new behaviour Signed-off-by: Nic Colledge <nic@njcolledge.net> * Added more record types to rewrite handling based on PR/Issue feedback Signed-off-by: Nic Colledge <nic@njcolledge.net> * Updated README.md for plugin Signed-off-by: Nic Colledge <nic@njcolledge.net> * Updated unit tests. Small refactor of getTarget... function. Signed-off-by: Nic Colledge <nic@njcolledge.net> * Refactor to add response value rewrite as answer value option Signed-off-by: Nic Colledge <nic@njcolledge.net> * Removed TODO comment, added test for NAPTR record. Signed-off-by: Nic Colledge <nic@njcolledge.net>
Diffstat (limited to 'plugin')
-rw-r--r--plugin/rewrite/README.md17
-rw-r--r--plugin/rewrite/class.go4
-rw-r--r--plugin/rewrite/edns0.go16
-rw-r--r--plugin/rewrite/name.go104
-rw-r--r--plugin/rewrite/reverter.go138
-rw-r--r--plugin/rewrite/reverter_test.go87
-rw-r--r--plugin/rewrite/rewrite.go13
-rw-r--r--plugin/rewrite/ttl.go64
-rw-r--r--plugin/rewrite/type.go4
-rw-r--r--plugin/test/helpers.go3
10 files changed, 321 insertions, 129 deletions
diff --git a/plugin/rewrite/README.md b/plugin/rewrite/README.md
index ca48a6d17..2eecf748e 100644
--- a/plugin/rewrite/README.md
+++ b/plugin/rewrite/README.md
@@ -151,26 +151,37 @@ ftp-us-west-1.coredns.rocks. 0 IN A 10.20.20.20
ftp-us-west-1.coredns.rocks. 0 IN A 10.30.30.30
```
+It is also possible to rewrite other values returned in the DNS response records
+(e.g. the server names returned in `SRV` and `MX` records). This can be enabled by adding
+the `answer value` to a name regex rule as specified below. `answer value` takes a
+regular expression and a rewrite name as parameters and works in the same way as the
+`answer name` rule.
+
+Note that names in the `AUTHORITY SECTION` and `ADDITIONAL SECTION` will also be
+rewritten following the specified rules. The names returned by the following
+record types: `CNAME`, `DNAME`, `SOA`, `SRV`, `MX`, `NAPTR`, `NS` will be rewritten
+if the `answer value` rule is specified.
+
The syntax for the rewrite of DNS request and response is as follows:
```
rewrite [continue|stop] {
name regex STRING STRING
answer name STRING STRING
+ [answer value STRING STRING]
}
```
Note that the above syntax is strict. For response rewrites, only `name`
rules are allowed to match the question section, and only by match type
`regex`. The answer rewrite must be after the name, as in the
-syntax example. There must only be two lines (a `name` followed by an
-`answer`) in the brackets; additional rules are not supported.
+syntax example.
An alternate syntax for rewriting a DNS request and response is as
follows:
```
-rewrite [continue|stop] name regex STRING STRING answer name STRING STRING
+rewrite [continue|stop] name regex STRING STRING answer name STRING STRING [answer value STRING STRING]
```
When using `exact` name rewrite rules, the answer gets rewritten automatically,
diff --git a/plugin/rewrite/class.go b/plugin/rewrite/class.go
index 6906dcf6c..178cf8181 100644
--- a/plugin/rewrite/class.go
+++ b/plugin/rewrite/class.go
@@ -43,5 +43,5 @@ func (rule *classRule) Rewrite(ctx context.Context, state request.Request) Resul
// Mode returns the processing mode.
func (rule *classRule) Mode() string { return rule.NextAction }
-// GetResponseRule return a rule to rewrite the response with. Currently not implemented.
-func (rule *classRule) GetResponseRule() ResponseRule { return ResponseRule{} }
+// GetResponseRules return rules to rewrite the response with. Currently not implemented.
+func (rule *classRule) GetResponseRules() []ResponseRule { return []ResponseRule{} }
diff --git a/plugin/rewrite/edns0.go b/plugin/rewrite/edns0.go
index c8d480ba9..fd355d678 100644
--- a/plugin/rewrite/edns0.go
+++ b/plugin/rewrite/edns0.go
@@ -73,8 +73,8 @@ func (rule *edns0NsidRule) Rewrite(ctx context.Context, state request.Request) R
// Mode returns the processing mode.
func (rule *edns0NsidRule) Mode() string { return rule.mode }
-// GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
-func (rule *edns0NsidRule) GetResponseRule() ResponseRule { return ResponseRule{} }
+// GetResponseRules returns rules to rewrite the response with. Currently not implemented.
+func (rule *edns0NsidRule) GetResponseRules() []ResponseRule { return []ResponseRule{} }
// Rewrite will alter the request EDNS0 local options.
func (rule *edns0LocalRule) Rewrite(ctx context.Context, state request.Request) Result {
@@ -103,8 +103,8 @@ func (rule *edns0LocalRule) Rewrite(ctx context.Context, state request.Request)
// Mode returns the processing mode.
func (rule *edns0LocalRule) Mode() string { return rule.mode }
-// GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
-func (rule *edns0LocalRule) GetResponseRule() ResponseRule { return ResponseRule{} }
+// GetResponseRules returns a rule to rewrite the response with. Currently not implemented.
+func (rule *edns0LocalRule) GetResponseRules() []ResponseRule { return []ResponseRule{} }
// newEdns0Rule creates an EDNS0 rule of the appropriate type based on the args
func newEdns0Rule(mode string, args ...string) (Rule, error) {
@@ -253,8 +253,8 @@ func (rule *edns0VariableRule) Rewrite(ctx context.Context, state request.Reques
// Mode returns the processing mode.
func (rule *edns0VariableRule) Mode() string { return rule.mode }
-// GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
-func (rule *edns0VariableRule) GetResponseRule() ResponseRule { return ResponseRule{} }
+// GetResponseRules returns rules to rewrite the response with. Currently not implemented.
+func (rule *edns0VariableRule) GetResponseRules() []ResponseRule { return []ResponseRule{} }
func isValidVariable(variable string) bool {
switch variable {
@@ -362,8 +362,8 @@ func (rule *edns0SubnetRule) Rewrite(ctx context.Context, state request.Request)
// Mode returns the processing mode
func (rule *edns0SubnetRule) Mode() string { return rule.mode }
-// GetResponseRule return a rule to rewrite the response with. Currently not implemented.
-func (rule *edns0SubnetRule) GetResponseRule() ResponseRule { return ResponseRule{} }
+// GetResponseRules return rules to rewrite the response with. Currently not implemented.
+func (rule *edns0SubnetRule) GetResponseRules() []ResponseRule { return []ResponseRule{} }
// These are all defined actions.
const (
diff --git a/plugin/rewrite/name.go b/plugin/rewrite/name.go
index c55c7d1ff..1f43d15b8 100644
--- a/plugin/rewrite/name.go
+++ b/plugin/rewrite/name.go
@@ -37,10 +37,10 @@ type substringNameRule struct {
}
type regexNameRule struct {
- NextAction string
- Pattern *regexp.Regexp
- Replacement string
- ResponseRule
+ NextAction string
+ Pattern *regexp.Regexp
+ Replacement string
+ ResponseRules []ResponseRule
}
const (
@@ -113,7 +113,6 @@ func (rule *regexNameRule) Rewrite(ctx context.Context, state request.Request) R
// newNameRule creates a name matching rule based on exact, partial, or regex match
func newNameRule(nextAction string, args ...string) (Rule, error) {
var matchType, rewriteQuestionFrom, rewriteQuestionTo string
- var rewriteAnswerField, rewriteAnswerFrom, rewriteAnswerTo string
if len(args) < 2 {
return nil, fmt.Errorf("too few arguments for a name rule")
}
@@ -140,8 +139,9 @@ func newNameRule(nextAction string, args ...string) (Rule, error) {
}
}
- if len(args) > 3 && len(args) != 7 {
- return nil, fmt.Errorf("response rewrites must consist only of a name rule with 3 arguments and an answer rule with 3 arguments")
+ //if len(args) > 3 && len(args) != 7 {
+ if len(args) > 3 && (len(args) - 3) % 4 != 0 {
+ return nil, fmt.Errorf("response rewrites must consist only of a name rule with 3 arguments and one or more answer rules with 3 arguments each")
}
if len(args) < 7 {
@@ -190,47 +190,39 @@ func newNameRule(nextAction string, args ...string) (Rule, error) {
nextAction,
rewriteQuestionFromPattern,
rewriteQuestionTo,
- ResponseRule{
+ []ResponseRule{{
Type: "name",
- },
+ }},
}, nil
default:
return nil, fmt.Errorf("name rule supports only exact, prefix, suffix, substring, and regex name matching, received: %s", matchType)
}
}
- if len(args) == 7 {
+ //if len(args) == 7 {
+ if (len(args) - 3) % 4 == 0 {
if matchType == RegexMatch {
- if args[3] != "answer" {
- return nil, fmt.Errorf("exceeded the number of arguments for a regex name rule")
- }
rewriteQuestionFromPattern, err := isValidRegexPattern(rewriteQuestionFrom, rewriteQuestionTo)
if err != nil {
return nil, err
}
- rewriteAnswerField = strings.ToLower(args[4])
- switch rewriteAnswerField {
- case "name":
- default:
- return nil, fmt.Errorf("exceeded the number of arguments for a regex name rule")
- }
- rewriteAnswerFrom = args[5]
- rewriteAnswerTo = args[6]
- rewriteAnswerFromPattern, err := isValidRegexPattern(rewriteAnswerFrom, rewriteAnswerTo)
- if err != nil {
- return nil, err
- }
rewriteQuestionTo = plugin.Name(args[2]).Normalize()
- rewriteAnswerTo = plugin.Name(args[6]).Normalize()
+
+ responseRuleCount := (len(args) - 3) / 4
+ responseRules := make([]ResponseRule, responseRuleCount)
+ for i := 0; i < responseRuleCount; i ++ {
+ startIdx := 3 + (i * 4)
+ responseRule, err := newResponseRule(args[startIdx:startIdx + 4])
+ if err != nil {
+ return nil, err
+ }
+ responseRules[i] = *responseRule
+ }
+
return &regexNameRule{
nextAction,
rewriteQuestionFromPattern,
rewriteQuestionTo,
- ResponseRule{
- Active: true,
- Type: "name",
- Pattern: rewriteAnswerFromPattern,
- Replacement: rewriteAnswerTo,
- },
+ responseRules,
}, nil
}
return nil, fmt.Errorf("the rewrite of response is supported only for name regex rule")
@@ -238,6 +230,34 @@ func newNameRule(nextAction string, args ...string) (Rule, error) {
return nil, fmt.Errorf("the rewrite rule is invalid: %s", args)
}
+// newResponseRule creates a new "answer name" or "answer value" response rule.
+func newResponseRule(args []string) (responseRule *ResponseRule, err error){
+ if args[0] != "answer" {
+ return nil, fmt.Errorf("exceeded the number of arguments for a regex name rule")
+ }
+ rewriteAnswerField := strings.ToLower(args[1])
+ switch rewriteAnswerField {
+ case "name":
+ case "value":
+ default:
+ return nil, fmt.Errorf("exceeded the number of arguments for a regex name rule")
+ }
+ rewriteAnswerFrom := args[2]
+ rewriteAnswerTo := args[3]
+ rewriteAnswerFromPattern, err := isValidRegexPattern(rewriteAnswerFrom, rewriteAnswerTo)
+ if err != nil {
+ return nil, err
+ }
+ rewriteAnswerTo = plugin.Name(args[3]).Normalize()
+
+ return &ResponseRule{
+ Active: true,
+ Type: rewriteAnswerField,
+ Pattern: rewriteAnswerFromPattern,
+ Replacement: rewriteAnswerTo,
+ }, nil
+}
+
// Mode returns the processing nextAction
func (rule *exactNameRule) Mode() string { return rule.NextAction }
func (rule *prefixNameRule) Mode() string { return rule.NextAction }
@@ -245,20 +265,20 @@ func (rule *suffixNameRule) Mode() string { return rule.NextAction }
func (rule *substringNameRule) Mode() string { return rule.NextAction }
func (rule *regexNameRule) Mode() string { return rule.NextAction }
-// GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
-func (rule *exactNameRule) GetResponseRule() ResponseRule { return rule.ResponseRule }
+// GetResponseRules returns rules to rewrite the response with. Currently not implemented.
+func (rule *exactNameRule) GetResponseRules() []ResponseRule { return []ResponseRule{rule.ResponseRule} }
-// GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
-func (rule *prefixNameRule) GetResponseRule() ResponseRule { return ResponseRule{} }
+// GetResponseRules returns rules to rewrite the response with. Currently not implemented.
+func (rule *prefixNameRule) GetResponseRules() []ResponseRule { return []ResponseRule{} }
-// GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
-func (rule *suffixNameRule) GetResponseRule() ResponseRule { return ResponseRule{} }
+// GetResponseRules returns rules to rewrite the response with. Currently not implemented.
+func (rule *suffixNameRule) GetResponseRules() []ResponseRule { return []ResponseRule{} }
-// GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
-func (rule *substringNameRule) GetResponseRule() ResponseRule { return ResponseRule{} }
+// GetResponseRules returns rules to rewrite the response with. Currently not implemented.
+func (rule *substringNameRule) GetResponseRules() []ResponseRule { return []ResponseRule{} }
-// GetResponseRule returns a rule to rewrite the response with.
-func (rule *regexNameRule) GetResponseRule() ResponseRule { return rule.ResponseRule }
+// GetResponseRules returns rules to rewrite the response with.
+func (rule *regexNameRule) GetResponseRules() []ResponseRule { return rule.ResponseRules }
// hasClosingDot returns true if s has a closing dot at the end.
func hasClosingDot(s string) bool {
diff --git a/plugin/rewrite/reverter.go b/plugin/rewrite/reverter.go
index 9b1de0563..846e9624b 100644
--- a/plugin/rewrite/reverter.go
+++ b/plugin/rewrite/reverter.go
@@ -42,46 +42,116 @@ func (r *ResponseReverter) WriteMsg(res1 *dns.Msg) error {
res.Question[0] = r.originalQuestion
if r.ResponseRewrite {
+ for _, rr := range res.Ns {
+ rewriteResourceRecord(res, rr, r)
+ }
+
for _, rr := range res.Answer {
- var (
- isNameRewritten bool
- isTTLRewritten bool
- name = rr.Header().Name
- ttl = rr.Header().Ttl
- )
- for _, rule := range r.ResponseRules {
- if rule.Type == "" {
- rule.Type = "name"
- }
- switch rule.Type {
- case "name":
- regexGroups := rule.Pattern.FindStringSubmatch(name)
- if len(regexGroups) == 0 {
- continue
- }
- s := rule.Replacement
- for groupIndex, groupValue := range regexGroups {
- groupIndexStr := "{" + strconv.Itoa(groupIndex) + "}"
- s = strings.Replace(s, groupIndexStr, groupValue, -1)
- }
- name = s
- isNameRewritten = true
- case "ttl":
- ttl = rule.TTL
- isTTLRewritten = true
- }
- }
- if isNameRewritten {
- rr.Header().Name = name
- }
- if isTTLRewritten {
- rr.Header().Ttl = ttl
- }
+ rewriteResourceRecord(res, rr, r)
+ }
+
+ for _, rr := range res.Extra {
+ rewriteResourceRecord(res, rr, r)
}
+
}
return r.ResponseWriter.WriteMsg(res)
}
+func rewriteResourceRecord(res *dns.Msg, rr dns.RR, r *ResponseReverter) {
+ var (
+ isNameRewritten bool
+ isTTLRewritten bool
+ isValueRewritten bool
+ name = rr.Header().Name
+ ttl = rr.Header().Ttl
+ value string
+ )
+
+ for _, rule := range r.ResponseRules {
+ if rule.Type == "" {
+ rule.Type = "name"
+ }
+ switch rule.Type {
+ case "name":
+ rewriteString(rule, &name, &isNameRewritten)
+ case "value":
+ value = getRecordValueForRewrite(rr)
+ if value != "" {
+ rewriteString(rule, &value, &isValueRewritten)
+ }
+ case "ttl":
+ ttl = rule.TTL
+ isTTLRewritten = true
+ }
+ }
+
+ if isNameRewritten {
+ rr.Header().Name = name
+ }
+ if isTTLRewritten {
+ rr.Header().Ttl = ttl
+ }
+ if isValueRewritten {
+ setRewrittenRecordValue(rr, value)
+ }
+}
+
+func getRecordValueForRewrite(rr dns.RR) (name string) {
+ switch rr.Header().Rrtype {
+ case dns.TypeSRV:
+ return rr.(*dns.SRV).Target
+ case dns.TypeMX:
+ return rr.(*dns.MX).Mx
+ case dns.TypeCNAME:
+ return rr.(*dns.CNAME).Target
+ case dns.TypeNS:
+ return rr.(*dns.NS).Ns
+ case dns.TypeDNAME:
+ return rr.(*dns.DNAME).Target
+ case dns.TypeNAPTR:
+ return rr.(*dns.NAPTR).Replacement
+ case dns.TypeSOA:
+ return rr.(*dns.SOA).Ns
+ default:
+ return ""
+ }
+}
+
+func setRewrittenRecordValue(rr dns.RR, value string) {
+ switch rr.Header().Rrtype {
+ case dns.TypeSRV:
+ rr.(*dns.SRV).Target = value
+ case dns.TypeMX:
+ rr.(*dns.MX).Mx = value
+ case dns.TypeCNAME:
+ rr.(*dns.CNAME).Target = value
+ case dns.TypeNS:
+ rr.(*dns.NS).Ns = value
+ case dns.TypeDNAME:
+ rr.(*dns.DNAME).Target = value
+ case dns.TypeNAPTR:
+ rr.(*dns.NAPTR).Replacement = value
+ case dns.TypeSOA:
+ rr.(*dns.SOA).Ns = value
+ }
+}
+
+func rewriteString(rule ResponseRule, str *string, isStringRewritten *bool) {
+ regexGroups := rule.Pattern.FindStringSubmatch(*str)
+ if len(regexGroups) == 0 {
+ return
+ }
+ s := rule.Replacement
+ for groupIndex, groupValue := range regexGroups {
+ groupIndexStr := "{" + strconv.Itoa(groupIndex) + "}"
+ s = strings.Replace(s, groupIndexStr, groupValue, -1)
+ }
+
+ *isStringRewritten = true
+ *str = s
+}
+
// Write is a wrapper that records the size of the message that gets written.
func (r *ResponseReverter) Write(buf []byte) (int, error) {
n, err := r.ResponseWriter.Write(buf)
diff --git a/plugin/rewrite/reverter_test.go b/plugin/rewrite/reverter_test.go
index d46180af4..d0e0cfe4d 100644
--- a/plugin/rewrite/reverter_test.go
+++ b/plugin/rewrite/reverter_test.go
@@ -68,3 +68,90 @@ func doReverterTests(rules []Rule, t *testing.T) {
}
}
}
+
+var valueTests = []struct {
+ from string
+ fromType uint16
+ answer []dns.RR
+ extra []dns.RR
+ to string
+ toType uint16
+ noRevert bool
+ expectValue string
+ expectAnswerType uint16
+ expectAddlName string
+}{
+ {"my.domain.uk", dns.TypeSRV, []dns.RR{test.SRV("my.cluster.local. 5 IN SRV 0 100 100 srv1.my.cluster.local.")}, []dns.RR{test.A("srv1.my.cluster.local. 5 IN A 10.0.0.1")}, "my.domain.uk", dns.TypeSRV, false, "srv1.my.domain.uk.", dns.TypeSRV, "srv1.my.domain.uk."},
+ {"my.domain.uk", dns.TypeSRV, []dns.RR{test.SRV("my.cluster.local. 5 IN SRV 0 100 100 srv1.my.cluster.local.")}, []dns.RR{test.A("srv1.my.cluster.local. 5 IN A 10.0.0.1")}, "my.cluster.local.", dns.TypeSRV, true, "srv1.my.cluster.local.", dns.TypeSRV, "srv1.my.cluster.local."},
+ {"my.domain.uk", dns.TypeANY, []dns.RR{test.CNAME("my.cluster.local. 3600 IN CNAME cname.cluster.local.")}, []dns.RR{test.A("cname.cluster.local. 5 IN A 10.0.0.1")}, "my.domain.uk", dns.TypeANY, false, "cname.domain.uk.", dns.TypeCNAME, "cname.domain.uk."},
+ {"my.domain.uk", dns.TypeANY, []dns.RR{test.CNAME("my.cluster.local. 3600 IN CNAME cname.cluster.local.")}, []dns.RR{test.A("cname.cluster.local. 5 IN A 10.0.0.1")}, "my.cluster.local.", dns.TypeANY, true, "cname.cluster.local.", dns.TypeCNAME, "cname.cluster.local."},
+ {"my.domain.uk", dns.TypeANY, []dns.RR{test.DNAME("my.cluster.local. 3600 IN DNAME dname.cluster.local.")}, []dns.RR{test.A("dname.cluster.local. 5 IN A 10.0.0.1")}, "my.domain.uk", dns.TypeANY, false, "dname.domain.uk.", dns.TypeDNAME, "dname.domain.uk."},
+ {"my.domain.uk", dns.TypeANY, []dns.RR{test.DNAME("my.cluster.local. 3600 IN DNAME dname.cluster.local.")}, []dns.RR{test.A("dname.cluster.local. 5 IN A 10.0.0.1")}, "my.cluster.local.", dns.TypeANY, true, "dname.cluster.local.", dns.TypeDNAME, "dname.cluster.local."},
+ {"my.domain.uk", dns.TypeMX, []dns.RR{test.MX("my.cluster.local. 3600 IN MX 1 mx1.cluster.local.")}, []dns.RR{test.A("mx1.cluster.local. 5 IN A 10.0.0.1")}, "my.domain.uk", dns.TypeMX, false, "mx1.domain.uk.", dns.TypeMX, "mx1.domain.uk."},
+ {"my.domain.uk", dns.TypeMX, []dns.RR{test.MX("my.cluster.local. 3600 IN MX 1 mx1.cluster.local.")}, []dns.RR{test.A("mx1.cluster.local. 5 IN A 10.0.0.1")}, "my.cluster.local.", dns.TypeMX, true, "mx1.cluster.local.", dns.TypeMX, "mx1.cluster.local."},
+ {"my.domain.uk", dns.TypeANY, []dns.RR{test.NS("my.cluster.local. 3600 IN NS ns1.cluster.local.")}, []dns.RR{test.A("ns1.cluster.local. 5 IN A 10.0.0.1")}, "my.domain.uk", dns.TypeANY, false, "ns1.domain.uk.", dns.TypeNS, "ns1.domain.uk."},
+ {"my.domain.uk", dns.TypeANY, []dns.RR{test.NS("my.cluster.local. 3600 IN NS ns1.cluster.local.")}, []dns.RR{test.A("ns1.cluster.local. 5 IN A 10.0.0.1")}, "my.cluster.local.", dns.TypeANY, true, "ns1.cluster.local.", dns.TypeNS, "ns1.cluster.local."},
+ {"my.domain.uk", dns.TypeSOA, []dns.RR{test.SOA("my.cluster.local. 1800 IN SOA ns1.cluster.local. admin.cluster.local. 1502165581 14400 3600 604800 14400")}, []dns.RR{test.A("ns1.cluster.local. 5 IN A 10.0.0.1")}, "my.domain.uk", dns.TypeSOA, false, "ns1.domain.uk.", dns.TypeSOA, "ns1.domain.uk."},
+ {"my.domain.uk", dns.TypeSOA, []dns.RR{test.SOA("my.cluster.local. 1800 IN SOA ns1.cluster.local. admin.cluster.local. 1502165581 14400 3600 604800 14400")}, []dns.RR{test.A("ns1.cluster.local. 5 IN A 10.0.0.1")}, "my.cluster.local.", dns.TypeSOA, true, "ns1.cluster.local.", dns.TypeSOA, "ns1.cluster.local."},
+ {"my.domain.uk", dns.TypeNAPTR, []dns.RR{test.NAPTR("my.cluster.local. 100 IN NAPTR 100 10 \"S\" \"SIP+D2U\" \"!^.*$!sip:customer-service@example.com!\" _sip._udp.cluster.local.")}, []dns.RR{test.A("ns1.cluster.local. 5 IN A 10.0.0.1")}, "my.domain.uk", dns.TypeNAPTR, false, "_sip._udp.domain.uk.", dns.TypeNAPTR, "ns1.domain.uk."},
+ {"my.domain.uk", dns.TypeNAPTR, []dns.RR{test.NAPTR("my.cluster.local. 100 IN NAPTR 100 10 \"S\" \"SIP+D2U\" \"!^.*$!sip:customer-service@example.com!\" _sip._udp.cluster.local.")}, []dns.RR{test.A("ns1.cluster.local. 5 IN A 10.0.0.1")}, "my.cluster.local.", dns.TypeNAPTR, true, "_sip._udp.cluster.local.", dns.TypeNAPTR, "ns1.cluster.local."},
+}
+
+func TestValueResponseReverter(t *testing.T) {
+
+ rules := []Rule{}
+ r, _ := newNameRule("stop", "regex", `(.*)\.domain\.uk`, "{1}.cluster.local", "answer", "name", `(.*)\.cluster\.local`, "{1}.domain.uk", "answer", "value", `(.*)\.cluster\.local`, "{1}.domain.uk")
+ rules = append(rules, r)
+
+ doValueReverterTests(rules, t)
+
+ rules = []Rule{}
+ r, _ = newNameRule("continue", "regex", `(.*)\.domain\.uk`, "{1}.cluster.local", "answer", "name", `(.*)\.cluster\.local`, "{1}.domain.uk", "answer", "value", `(.*)\.cluster\.local`, "{1}.domain.uk")
+ rules = append(rules, r)
+
+ doValueReverterTests(rules, t)
+}
+
+func doValueReverterTests(rules []Rule, t *testing.T) {
+ ctx := context.TODO()
+ for i, tc := range valueTests {
+ m := new(dns.Msg)
+ m.SetQuestion(tc.from, tc.fromType)
+ m.Question[0].Qclass = dns.ClassINET
+ m.Answer = tc.answer
+ m.Extra = tc.extra
+ rw := Rewrite{
+ Next: plugin.HandlerFunc(msgPrinter),
+ Rules: rules,
+ noRevert: tc.noRevert,
+ }
+ rec := dnstest.NewRecorder(&test.ResponseWriter{})
+ rw.ServeDNS(ctx, rec, m)
+ resp := rec.Msg
+ if resp.Question[0].Name != tc.to {
+ t.Errorf("Test %d: Expected Name to be %q but was %q", i, tc.to, resp.Question[0].Name)
+ }
+ if resp.Question[0].Qtype != tc.toType {
+ t.Errorf("Test %d: Expected Type to be '%d' but was '%d'", i, tc.toType, resp.Question[0].Qtype)
+ }
+
+ if len(resp.Answer) <= 0 || resp.Answer[0].Header().Rrtype != tc.expectAnswerType {
+ t.Error("Unexpected Answer Record Type / No Answers")
+ return
+ }
+
+ value := getRecordValueForRewrite(resp.Answer[0])
+ if value != tc.expectValue {
+ t.Errorf("Test %d: Expected Target to be '%s' but was '%s'", i, tc.expectValue, value)
+ }
+
+ if len(resp.Extra) <= 0 || resp.Extra[0].Header().Rrtype != dns.TypeA {
+ t.Error("Unexpected Additional Record Type / No Additional Records")
+ return
+ }
+
+ if resp.Extra[0].Header().Name != tc.expectAddlName {
+ t.Errorf("Test %d: Expected Extra Name to be %q but was %q", i, tc.expectAddlName, resp.Extra[0].Header().Name)
+ }
+ }
+}
diff --git a/plugin/rewrite/rewrite.go b/plugin/rewrite/rewrite.go
index 13e1d2092..f44cc62f1 100644
--- a/plugin/rewrite/rewrite.go
+++ b/plugin/rewrite/rewrite.go
@@ -49,10 +49,11 @@ func (rw Rewrite) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg
state.Req.Question[0] = wr.originalQuestion
return dns.RcodeServerFailure, err
}
- respRule := rule.GetResponseRule()
- if respRule.Active {
- wr.ResponseRewrite = true
- wr.ResponseRules = append(wr.ResponseRules, respRule)
+ for _, respRule := range rule.GetResponseRules() {
+ if respRule.Active {
+ wr.ResponseRewrite = true
+ wr.ResponseRules = append(wr.ResponseRules, respRule)
+ }
}
if rule.Mode() == Stop {
if rw.noRevert {
@@ -78,8 +79,8 @@ type Rule interface {
Rewrite(ctx context.Context, state request.Request) Result
// Mode returns the processing mode stop or continue.
Mode() string
- // GetResponseRule returns the rule to rewrite response with, if any.
- GetResponseRule() ResponseRule
+ // GetResponseRules returns rules to rewrite response with, if any.
+ GetResponseRules() []ResponseRule
}
func newRule(args ...string) (Rule, error) {
diff --git a/plugin/rewrite/ttl.go b/plugin/rewrite/ttl.go
index 50ace9c14..999f03547 100644
--- a/plugin/rewrite/ttl.go
+++ b/plugin/rewrite/ttl.go
@@ -15,31 +15,31 @@ import (
type exactTTLRule struct {
NextAction string
From string
- ResponseRule
+ ResponseRules []ResponseRule
}
type prefixTTLRule struct {
NextAction string
Prefix string
- ResponseRule
+ ResponseRules []ResponseRule
}
type suffixTTLRule struct {
NextAction string
Suffix string
- ResponseRule
+ ResponseRules []ResponseRule
}
type substringTTLRule struct {
NextAction string
Substring string
- ResponseRule
+ ResponseRules []ResponseRule
}
type regexTTLRule struct {
NextAction string
Pattern *regexp.Regexp
- ResponseRule
+ ResponseRules []ResponseRule
}
// Rewrite rewrites the current request based upon exact match of the name
@@ -108,41 +108,41 @@ func newTTLRule(nextAction string, args ...string) (Rule, error) {
return &exactTTLRule{
nextAction,
plugin.Name(args[1]).Normalize(),
- ResponseRule{
+ []ResponseRule{{
Active: true,
Type: "ttl",
TTL: ttl,
- },
+ }},
}, nil
case PrefixMatch:
return &prefixTTLRule{
nextAction,
plugin.Name(args[1]).Normalize(),
- ResponseRule{
+ []ResponseRule{{
Active: true,
Type: "ttl",
TTL: ttl,
- },
+ }},
}, nil
case SuffixMatch:
return &suffixTTLRule{
nextAction,
plugin.Name(args[1]).Normalize(),
- ResponseRule{
+ []ResponseRule{{
Active: true,
Type: "ttl",
TTL: ttl,
- },
+ }},
}, nil
case SubstringMatch:
return &substringTTLRule{
nextAction,
plugin.Name(args[1]).Normalize(),
- ResponseRule{
+ []ResponseRule{{
Active: true,
Type: "ttl",
TTL: ttl,
- },
+ }},
}, nil
case RegexMatch:
regexPattern, err := regexp.Compile(args[1])
@@ -152,11 +152,11 @@ func newTTLRule(nextAction string, args ...string) (Rule, error) {
return &regexTTLRule{
nextAction,
regexPattern,
- ResponseRule{
+ []ResponseRule{{
Active: true,
Type: "ttl",
TTL: ttl,
- },
+ }},
}, nil
default:
return nil, fmt.Errorf("ttl rule supports only exact, prefix, suffix, substring, and regex name matching")
@@ -168,11 +168,11 @@ func newTTLRule(nextAction string, args ...string) (Rule, error) {
return &exactTTLRule{
nextAction,
plugin.Name(args[0]).Normalize(),
- ResponseRule{
+ []ResponseRule{{
Active: true,
Type: "ttl",
TTL: ttl,
- },
+ }},
}, nil
}
@@ -183,29 +183,29 @@ func (rule *suffixTTLRule) Mode() string { return rule.NextAction }
func (rule *substringTTLRule) Mode() string { return rule.NextAction }
func (rule *regexTTLRule) Mode() string { return rule.NextAction }
-// GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
-func (rule *exactTTLRule) GetResponseRule() ResponseRule {
- return rule.ResponseRule
+// GetResponseRules returns rules to rewrite the response with. Currently not implemented.
+func (rule *exactTTLRule) GetResponseRules() []ResponseRule {
+ return rule.ResponseRules
}
-// GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
-func (rule *prefixTTLRule) GetResponseRule() ResponseRule {
- return rule.ResponseRule
+// GetResponseRules returns rules to rewrite the response with. Currently not implemented.
+func (rule *prefixTTLRule) GetResponseRules() []ResponseRule {
+ return rule.ResponseRules
}
-// GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
-func (rule *suffixTTLRule) GetResponseRule() ResponseRule {
- return rule.ResponseRule
+// GetResponseRules returns rules to rewrite the response with. Currently not implemented.
+func (rule *suffixTTLRule) GetResponseRules() []ResponseRule {
+ return rule.ResponseRules
}
-// GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
-func (rule *substringTTLRule) GetResponseRule() ResponseRule {
- return rule.ResponseRule
+// GetResponseRules returns rules to rewrite the response with. Currently not implemented.
+func (rule *substringTTLRule) GetResponseRules() []ResponseRule {
+ return rule.ResponseRules
}
-// GetResponseRule returns a rule to rewrite the response with.
-func (rule *regexTTLRule) GetResponseRule() ResponseRule {
- return rule.ResponseRule
+// GetResponseRules returns rules to rewrite the response with.
+func (rule *regexTTLRule) GetResponseRules() []ResponseRule {
+ return rule.ResponseRules
}
// validTTL returns true if v is valid TTL value.
diff --git a/plugin/rewrite/type.go b/plugin/rewrite/type.go
index 91fc593a3..44f5047b5 100644
--- a/plugin/rewrite/type.go
+++ b/plugin/rewrite/type.go
@@ -44,5 +44,5 @@ func (rule *typeRule) Rewrite(ctx context.Context, state request.Request) Result
// Mode returns the processing mode.
func (rule *typeRule) Mode() string { return rule.nextAction }
-// GetResponseRule return a rule to rewrite the response with. Currently not implemented.
-func (rule *typeRule) GetResponseRule() ResponseRule { return ResponseRule{} }
+// GetResponseRules return rules to rewrite the response with. Currently not implemented.
+func (rule *typeRule) GetResponseRules() []ResponseRule { return []ResponseRule{} }
diff --git a/plugin/test/helpers.go b/plugin/test/helpers.go
index fa89f9218..0c7e85f2a 100644
--- a/plugin/test/helpers.go
+++ b/plugin/test/helpers.go
@@ -99,6 +99,9 @@ func DNSKEY(rr string) *dns.DNSKEY { r, _ := dns.NewRR(rr); return r.(*dns.DNSKE
// DS returns a DS record from rr. It panics on errors.
func DS(rr string) *dns.DS { r, _ := dns.NewRR(rr); return r.(*dns.DS) }
+// NAPTR returns a NAPTR record from rr. It panics on errors.
+func NAPTR(rr string) *dns.NAPTR { r, _ := dns.NewRR(rr); return r.(*dns.NAPTR) }
+
// OPT returns an OPT record with UDP buffer size set to bufsize and the DO bit set to do.
func OPT(bufsize int, do bool) *dns.OPT {
o := new(dns.OPT)