plugin/rewrite: add closing dot for suffix rewrite rule (#2070)

* add closing dot for suffix rewrite rule
* improve rule syntax checks

Resolves: #1881
This commit is contained in:
Paul G
2018-10-23 16:59:59 -04:00
committed by GitHub
parent e8e8187a64
commit a9ce35ae4e
4 changed files with 200 additions and 38 deletions

View File

@@ -13,10 +13,11 @@ import (
"github.com/miekg/dns"
)
type nameRule struct {
type exactNameRule struct {
NextAction string
From string
To string
ResponseRule
}
type prefixNameRule struct {
@@ -59,7 +60,7 @@ const (
// Rewrite rewrites the current request based upon exact match of the name
// in the question section of the request.
func (rule *nameRule) Rewrite(ctx context.Context, state request.Request) Result {
func (rule *exactNameRule) Rewrite(ctx context.Context, state request.Request) Result {
if rule.From == state.Name() {
state.Req.Question[0].Name = rule.To
return RewriteDone
@@ -115,76 +116,141 @@ 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")
}
if len(args) == 3 {
switch strings.ToLower(args[0]) {
if len(args) == 2 {
matchType = "exact"
rewriteQuestionFrom = plugin.Name(args[0]).Normalize()
rewriteQuestionTo = plugin.Name(args[1]).Normalize()
}
if len(args) >= 3 {
matchType = strings.ToLower(args[0])
rewriteQuestionFrom = plugin.Name(args[1]).Normalize()
rewriteQuestionTo = plugin.Name(args[2]).Normalize()
}
if matchType == RegexMatch {
rewriteQuestionFrom = args[1]
rewriteQuestionTo = args[2]
}
if matchType == ExactMatch || matchType == SuffixMatch {
if !hasClosingDot(rewriteQuestionFrom) {
rewriteQuestionFrom = rewriteQuestionFrom + "."
}
if !hasClosingDot(rewriteQuestionTo) {
rewriteQuestionTo = rewriteQuestionTo + "."
}
}
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) < 7 {
switch matchType {
case ExactMatch:
return &nameRule{nextAction, plugin.Name(args[1]).Normalize(), plugin.Name(args[2]).Normalize()}, nil
case PrefixMatch:
return &prefixNameRule{nextAction, plugin.Name(args[1]).Normalize(), plugin.Name(args[2]).Normalize()}, nil
case SuffixMatch:
return &suffixNameRule{nextAction, plugin.Name(args[1]).Normalize(), plugin.Name(args[2]).Normalize()}, nil
case SubstringMatch:
return &substringNameRule{nextAction, plugin.Name(args[1]).Normalize(), plugin.Name(args[2]).Normalize()}, nil
case RegexMatch:
regexPattern, err := regexp.Compile(args[1])
rewriteAnswerFromPattern, err := isValidRegexPattern(rewriteQuestionTo, rewriteQuestionFrom)
if err != nil {
return nil, fmt.Errorf("Invalid regex pattern in a name rule: %s", args[1])
return nil, err
}
return &regexNameRule{nextAction, regexPattern, plugin.Name(args[2]).Normalize(), ResponseRule{Type: "name"}}, nil
return &exactNameRule{
nextAction,
rewriteQuestionFrom,
rewriteQuestionTo,
ResponseRule{
Active: true,
Type: "name",
Pattern: rewriteAnswerFromPattern,
Replacement: rewriteQuestionFrom,
},
}, nil
case PrefixMatch:
return &prefixNameRule{
nextAction,
rewriteQuestionFrom,
rewriteQuestionTo,
}, nil
case SuffixMatch:
return &suffixNameRule{
nextAction,
rewriteQuestionFrom,
rewriteQuestionTo,
}, nil
case SubstringMatch:
return &substringNameRule{
nextAction,
rewriteQuestionFrom,
rewriteQuestionTo,
}, nil
case RegexMatch:
rewriteQuestionFromPattern, err := isValidRegexPattern(rewriteQuestionFrom, rewriteQuestionTo)
if err != nil {
return nil, err
}
rewriteQuestionTo := plugin.Name(args[2]).Normalize()
return &regexNameRule{
nextAction,
rewriteQuestionFromPattern,
rewriteQuestionTo,
ResponseRule{
Type: "name",
},
}, nil
default:
return nil, fmt.Errorf("A name rule supports only exact, prefix, suffix, substring, and regex name matching")
return nil, fmt.Errorf("A name rule supports only exact, prefix, suffix, substring, and regex name matching, received: %s", matchType)
}
}
if len(args) == 7 {
if strings.ToLower(args[0]) == RegexMatch {
if matchType == RegexMatch {
if args[3] != "answer" {
return nil, fmt.Errorf("exceeded the number of arguments for a regex name rule")
}
switch strings.ToLower(args[4]) {
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")
}
regexPattern, err := regexp.Compile(args[1])
rewriteAnswerFrom = args[5]
rewriteAnswerTo = args[6]
rewriteAnswerFromPattern, err := isValidRegexPattern(rewriteAnswerFrom, rewriteAnswerTo)
if err != nil {
return nil, fmt.Errorf("Invalid regex pattern in a name rule: %s", args)
}
responseRegexPattern, err := regexp.Compile(args[5])
if err != nil {
return nil, fmt.Errorf("Invalid regex pattern in a name rule: %s", args)
return nil, err
}
rewriteQuestionTo = plugin.Name(args[2]).Normalize()
rewriteAnswerTo = plugin.Name(args[6]).Normalize()
return &regexNameRule{
nextAction,
regexPattern,
plugin.Name(args[2]).Normalize(),
rewriteQuestionFromPattern,
rewriteQuestionTo,
ResponseRule{
Active: true,
Type: "name",
Pattern: responseRegexPattern,
Replacement: plugin.Name(args[6]).Normalize(),
Pattern: rewriteAnswerFromPattern,
Replacement: rewriteAnswerTo,
},
}, nil
}
return nil, fmt.Errorf("the rewrite of response is supported only for name regex rule")
}
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")
}
return &nameRule{nextAction, plugin.Name(args[0]).Normalize(), plugin.Name(args[1]).Normalize()}, nil
return nil, fmt.Errorf("the rewrite rule is invalid: %s", args)
}
// Mode returns the processing nextAction
func (rule *nameRule) Mode() string { return rule.NextAction }
func (rule *exactNameRule) Mode() string { return rule.NextAction }
func (rule *prefixNameRule) Mode() string { return rule.NextAction }
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 return a rule to rewrite the response with. Currently not implemented.
func (rule *nameRule) GetResponseRule() ResponseRule { return ResponseRule{} }
func (rule *exactNameRule) GetResponseRule() ResponseRule { return rule.ResponseRule }
// GetResponseRule return a rule to rewrite the response with. Currently not implemented.
func (rule *prefixNameRule) GetResponseRule() ResponseRule { return ResponseRule{} }
@@ -210,3 +276,34 @@ func validName(s string) bool {
return true
}
// hasClosingDot return true if s has a closing dot at the end.
func hasClosingDot(s string) bool {
if strings.HasSuffix(s, ".") {
return true
}
return false
}
// getSubExprUsage return the number of subexpressions used in s.
func getSubExprUsage(s string) int {
subExprUsage := 0
for i := 0; i <= 100; i++ {
if strings.Contains(s, "{"+strconv.Itoa(i)+"}") {
subExprUsage++
}
}
return subExprUsage
}
// isValidRegexPattern return a regular expression for pattern matching or errors, if any.
func isValidRegexPattern(rewriteFrom, rewriteTo string) (*regexp.Regexp, error) {
rewriteFromPattern, err := regexp.Compile(rewriteFrom)
if err != nil {
return nil, fmt.Errorf("Invalid regex matching pattern: %s", rewriteFrom)
}
if getSubExprUsage(rewriteTo) > rewriteFromPattern.NumSubexp() {
return nil, fmt.Errorf("The rewrite regex pattern (%s) uses more subexpressions than its corresponding matching regex pattern (%s)", rewriteTo, rewriteFrom)
}
return rewriteFromPattern, nil
}