mirror of
https://github.com/coredns/coredns.git
synced 2025-10-27 08:14:18 -04:00
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>
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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{} }
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 ®exNameRule{
|
||||
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 {
|
||||
|
||||
@@ -42,46 +42,116 @@ func (r *ResponseReverter) WriteMsg(res1 *dns.Msg) error {
|
||||
|
||||
res.Question[0] = r.originalQuestion
|
||||
if r.ResponseRewrite {
|
||||
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
|
||||
}
|
||||
for _, rr := range res.Ns {
|
||||
rewriteResourceRecord(res, rr, r)
|
||||
}
|
||||
|
||||
for _, rr := range res.Answer {
|
||||
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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 ®exTTLRule{
|
||||
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.
|
||||
|
||||
@@ -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{} }
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user