mirror of
https://github.com/coredns/coredns.git
synced 2026-10-22 02:05:42 -04:00
* plugin/rewrite: support opt-in metadata in regex name replacements Signed-off-by: houyuwushang <180804215+houyuwushang@users.noreply.github.com> * test: separate CoreDNS and third-party imports Signed-off-by: houyuwushang <180804215+houyuwushang@users.noreply.github.com> --------- Signed-off-by: houyuwushang <180804215+houyuwushang@users.noreply.github.com>
108 lines
2.9 KiB
Go
108 lines
2.9 KiB
Go
package rewrite
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/coredns/coredns/plugin/metadata"
|
|
"github.com/coredns/coredns/request"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
// metadataNameRule expands a replacement compiled at setup time. Values from
|
|
// captures and metadata are inserted literally, without interpreting them again.
|
|
type metadataNameRule struct {
|
|
nameRuleBase
|
|
pattern *regexp.Regexp
|
|
parts []nameReplacementPart
|
|
}
|
|
|
|
type nameReplacementPart struct {
|
|
literal string
|
|
label string
|
|
group int // -1 for literals and metadata labels
|
|
}
|
|
|
|
func newMetadataNameRule(nextAction string, auto bool, from, to string, answers ResponseRules) (Rule, error) {
|
|
pattern, err := isValidRegexPattern(from, "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
parts, err := parseNameReplacement(dns.Fqdn(to), pattern.NumSubexp())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &metadataNameRule{
|
|
nameRuleBase: newNameRuleBase(nextAction, auto, "", answers),
|
|
pattern: pattern,
|
|
parts: parts,
|
|
}, nil
|
|
}
|
|
|
|
func parseNameReplacement(replacement string, groups int) ([]nameReplacementPart, error) {
|
|
var parts []nameReplacementPart
|
|
for replacement != "" {
|
|
before, rest, found := strings.Cut(replacement, "{")
|
|
if strings.Contains(before, "}") {
|
|
return nil, fmt.Errorf("unmatched closing brace in metadata name replacement")
|
|
}
|
|
if before != "" {
|
|
parts = append(parts, nameReplacementPart{literal: strings.ToLower(before), group: -1})
|
|
}
|
|
if !found {
|
|
break
|
|
}
|
|
label, after, closed := strings.Cut(rest, "}")
|
|
if !closed {
|
|
return nil, fmt.Errorf("unclosed placeholder in metadata name replacement")
|
|
}
|
|
part := nameReplacementPart{group: -1}
|
|
if group, err := strconv.Atoi(label); err == nil {
|
|
if group < 0 || group > groups {
|
|
return nil, fmt.Errorf("capture group %s does not exist in name matching pattern", label)
|
|
}
|
|
part.group = group
|
|
} else {
|
|
if !metadata.IsLabel(label) || strings.ContainsAny(label, "{}") {
|
|
return nil, fmt.Errorf("invalid metadata label %q in name replacement", label)
|
|
}
|
|
part.label = label
|
|
}
|
|
parts = append(parts, part)
|
|
replacement = after
|
|
}
|
|
return parts, nil
|
|
}
|
|
|
|
func (rule *metadataNameRule) Rewrite(ctx context.Context, state request.Request) (ResponseRules, Result) {
|
|
groups := rule.pattern.FindStringSubmatch(state.Name())
|
|
if len(groups) == 0 {
|
|
return nil, RewriteIgnored
|
|
}
|
|
var replacement strings.Builder
|
|
for _, part := range rule.parts {
|
|
switch {
|
|
case part.group >= 0:
|
|
replacement.WriteString(groups[part.group])
|
|
case part.label != "":
|
|
value := metadata.ValueFunc(ctx, part.label)
|
|
if value == nil {
|
|
return nil, RewriteIgnored
|
|
}
|
|
text := value()
|
|
if text == "" {
|
|
return nil, RewriteIgnored
|
|
}
|
|
replacement.WriteString(strings.ToLower(text))
|
|
default:
|
|
replacement.WriteString(part.literal)
|
|
}
|
|
}
|
|
state.Req.Question[0].Name = replacement.String()
|
|
return rule.responseRuleFor(state)
|
|
}
|