/plugin/rewrite/name.go

https://github.com/coredns/coredns · Go · 289 lines · 241 code · 25 blank · 23 comment · 45 complexity · 0ad2583033d08588988e048ddd4b62c4 MD5 · raw file

  1. package rewrite
  2. import (
  3. "context"
  4. "fmt"
  5. "regexp"
  6. "strconv"
  7. "strings"
  8. "github.com/coredns/coredns/plugin"
  9. "github.com/coredns/coredns/request"
  10. )
  11. type exactNameRule struct {
  12. NextAction string
  13. From string
  14. To string
  15. ResponseRule
  16. }
  17. type prefixNameRule struct {
  18. NextAction string
  19. Prefix string
  20. Replacement string
  21. }
  22. type suffixNameRule struct {
  23. NextAction string
  24. Suffix string
  25. Replacement string
  26. }
  27. type substringNameRule struct {
  28. NextAction string
  29. Substring string
  30. Replacement string
  31. }
  32. type regexNameRule struct {
  33. NextAction string
  34. Pattern *regexp.Regexp
  35. Replacement string
  36. ResponseRule
  37. }
  38. const (
  39. // ExactMatch matches only on exact match of the name in the question section of a request
  40. ExactMatch = "exact"
  41. // PrefixMatch matches when the name begins with the matching string
  42. PrefixMatch = "prefix"
  43. // SuffixMatch matches when the name ends with the matching string
  44. SuffixMatch = "suffix"
  45. // SubstringMatch matches on partial match of the name in the question section of a request
  46. SubstringMatch = "substring"
  47. // RegexMatch matches when the name in the question section of a request matches a regular expression
  48. RegexMatch = "regex"
  49. )
  50. // Rewrite rewrites the current request based upon exact match of the name
  51. // in the question section of the request.
  52. func (rule *exactNameRule) Rewrite(ctx context.Context, state request.Request) Result {
  53. if rule.From == state.Name() {
  54. state.Req.Question[0].Name = rule.To
  55. return RewriteDone
  56. }
  57. return RewriteIgnored
  58. }
  59. // Rewrite rewrites the current request when the name begins with the matching string.
  60. func (rule *prefixNameRule) Rewrite(ctx context.Context, state request.Request) Result {
  61. if strings.HasPrefix(state.Name(), rule.Prefix) {
  62. state.Req.Question[0].Name = rule.Replacement + strings.TrimPrefix(state.Name(), rule.Prefix)
  63. return RewriteDone
  64. }
  65. return RewriteIgnored
  66. }
  67. // Rewrite rewrites the current request when the name ends with the matching string.
  68. func (rule *suffixNameRule) Rewrite(ctx context.Context, state request.Request) Result {
  69. if strings.HasSuffix(state.Name(), rule.Suffix) {
  70. state.Req.Question[0].Name = strings.TrimSuffix(state.Name(), rule.Suffix) + rule.Replacement
  71. return RewriteDone
  72. }
  73. return RewriteIgnored
  74. }
  75. // Rewrite rewrites the current request based upon partial match of the
  76. // name in the question section of the request.
  77. func (rule *substringNameRule) Rewrite(ctx context.Context, state request.Request) Result {
  78. if strings.Contains(state.Name(), rule.Substring) {
  79. state.Req.Question[0].Name = strings.Replace(state.Name(), rule.Substring, rule.Replacement, -1)
  80. return RewriteDone
  81. }
  82. return RewriteIgnored
  83. }
  84. // Rewrite rewrites the current request when the name in the question
  85. // section of the request matches a regular expression.
  86. func (rule *regexNameRule) Rewrite(ctx context.Context, state request.Request) Result {
  87. regexGroups := rule.Pattern.FindStringSubmatch(state.Name())
  88. if len(regexGroups) == 0 {
  89. return RewriteIgnored
  90. }
  91. s := rule.Replacement
  92. for groupIndex, groupValue := range regexGroups {
  93. groupIndexStr := "{" + strconv.Itoa(groupIndex) + "}"
  94. s = strings.Replace(s, groupIndexStr, groupValue, -1)
  95. }
  96. state.Req.Question[0].Name = s
  97. return RewriteDone
  98. }
  99. // newNameRule creates a name matching rule based on exact, partial, or regex match
  100. func newNameRule(nextAction string, args ...string) (Rule, error) {
  101. var matchType, rewriteQuestionFrom, rewriteQuestionTo string
  102. var rewriteAnswerField, rewriteAnswerFrom, rewriteAnswerTo string
  103. if len(args) < 2 {
  104. return nil, fmt.Errorf("too few arguments for a name rule")
  105. }
  106. if len(args) == 2 {
  107. matchType = "exact"
  108. rewriteQuestionFrom = plugin.Name(args[0]).Normalize()
  109. rewriteQuestionTo = plugin.Name(args[1]).Normalize()
  110. }
  111. if len(args) >= 3 {
  112. matchType = strings.ToLower(args[0])
  113. rewriteQuestionFrom = plugin.Name(args[1]).Normalize()
  114. rewriteQuestionTo = plugin.Name(args[2]).Normalize()
  115. }
  116. if matchType == RegexMatch {
  117. rewriteQuestionFrom = args[1]
  118. rewriteQuestionTo = args[2]
  119. }
  120. if matchType == ExactMatch || matchType == SuffixMatch {
  121. if !hasClosingDot(rewriteQuestionFrom) {
  122. rewriteQuestionFrom = rewriteQuestionFrom + "."
  123. }
  124. if !hasClosingDot(rewriteQuestionTo) {
  125. rewriteQuestionTo = rewriteQuestionTo + "."
  126. }
  127. }
  128. if len(args) > 3 && len(args) != 7 {
  129. return nil, fmt.Errorf("response rewrites must consist only of a name rule with 3 arguments and an answer rule with 3 arguments")
  130. }
  131. if len(args) < 7 {
  132. switch matchType {
  133. case ExactMatch:
  134. rewriteAnswerFromPattern, err := isValidRegexPattern(rewriteQuestionTo, rewriteQuestionFrom)
  135. if err != nil {
  136. return nil, err
  137. }
  138. return &exactNameRule{
  139. nextAction,
  140. rewriteQuestionFrom,
  141. rewriteQuestionTo,
  142. ResponseRule{
  143. Active: true,
  144. Type: "name",
  145. Pattern: rewriteAnswerFromPattern,
  146. Replacement: rewriteQuestionFrom,
  147. },
  148. }, nil
  149. case PrefixMatch:
  150. return &prefixNameRule{
  151. nextAction,
  152. rewriteQuestionFrom,
  153. rewriteQuestionTo,
  154. }, nil
  155. case SuffixMatch:
  156. return &suffixNameRule{
  157. nextAction,
  158. rewriteQuestionFrom,
  159. rewriteQuestionTo,
  160. }, nil
  161. case SubstringMatch:
  162. return &substringNameRule{
  163. nextAction,
  164. rewriteQuestionFrom,
  165. rewriteQuestionTo,
  166. }, nil
  167. case RegexMatch:
  168. rewriteQuestionFromPattern, err := isValidRegexPattern(rewriteQuestionFrom, rewriteQuestionTo)
  169. if err != nil {
  170. return nil, err
  171. }
  172. rewriteQuestionTo := plugin.Name(args[2]).Normalize()
  173. return &regexNameRule{
  174. nextAction,
  175. rewriteQuestionFromPattern,
  176. rewriteQuestionTo,
  177. ResponseRule{
  178. Type: "name",
  179. },
  180. }, nil
  181. default:
  182. return nil, fmt.Errorf("name rule supports only exact, prefix, suffix, substring, and regex name matching, received: %s", matchType)
  183. }
  184. }
  185. if len(args) == 7 {
  186. if matchType == RegexMatch {
  187. if args[3] != "answer" {
  188. return nil, fmt.Errorf("exceeded the number of arguments for a regex name rule")
  189. }
  190. rewriteQuestionFromPattern, err := isValidRegexPattern(rewriteQuestionFrom, rewriteQuestionTo)
  191. if err != nil {
  192. return nil, err
  193. }
  194. rewriteAnswerField = strings.ToLower(args[4])
  195. switch rewriteAnswerField {
  196. case "name":
  197. default:
  198. return nil, fmt.Errorf("exceeded the number of arguments for a regex name rule")
  199. }
  200. rewriteAnswerFrom = args[5]
  201. rewriteAnswerTo = args[6]
  202. rewriteAnswerFromPattern, err := isValidRegexPattern(rewriteAnswerFrom, rewriteAnswerTo)
  203. if err != nil {
  204. return nil, err
  205. }
  206. rewriteQuestionTo = plugin.Name(args[2]).Normalize()
  207. rewriteAnswerTo = plugin.Name(args[6]).Normalize()
  208. return &regexNameRule{
  209. nextAction,
  210. rewriteQuestionFromPattern,
  211. rewriteQuestionTo,
  212. ResponseRule{
  213. Active: true,
  214. Type: "name",
  215. Pattern: rewriteAnswerFromPattern,
  216. Replacement: rewriteAnswerTo,
  217. },
  218. }, nil
  219. }
  220. return nil, fmt.Errorf("the rewrite of response is supported only for name regex rule")
  221. }
  222. return nil, fmt.Errorf("the rewrite rule is invalid: %s", args)
  223. }
  224. // Mode returns the processing nextAction
  225. func (rule *exactNameRule) Mode() string { return rule.NextAction }
  226. func (rule *prefixNameRule) Mode() string { return rule.NextAction }
  227. func (rule *suffixNameRule) Mode() string { return rule.NextAction }
  228. func (rule *substringNameRule) Mode() string { return rule.NextAction }
  229. func (rule *regexNameRule) Mode() string { return rule.NextAction }
  230. // GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
  231. func (rule *exactNameRule) GetResponseRule() ResponseRule { return rule.ResponseRule }
  232. // GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
  233. func (rule *prefixNameRule) GetResponseRule() ResponseRule { return ResponseRule{} }
  234. // GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
  235. func (rule *suffixNameRule) GetResponseRule() ResponseRule { return ResponseRule{} }
  236. // GetResponseRule returns a rule to rewrite the response with. Currently not implemented.
  237. func (rule *substringNameRule) GetResponseRule() ResponseRule { return ResponseRule{} }
  238. // GetResponseRule returns a rule to rewrite the response with.
  239. func (rule *regexNameRule) GetResponseRule() ResponseRule { return rule.ResponseRule }
  240. // hasClosingDot returns true if s has a closing dot at the end.
  241. func hasClosingDot(s string) bool {
  242. return strings.HasSuffix(s, ".")
  243. }
  244. // getSubExprUsage returns the number of subexpressions used in s.
  245. func getSubExprUsage(s string) int {
  246. subExprUsage := 0
  247. for i := 0; i <= 100; i++ {
  248. if strings.Contains(s, "{"+strconv.Itoa(i)+"}") {
  249. subExprUsage++
  250. }
  251. }
  252. return subExprUsage
  253. }
  254. // isValidRegexPattern returns a regular expression for pattern matching or errors, if any.
  255. func isValidRegexPattern(rewriteFrom, rewriteTo string) (*regexp.Regexp, error) {
  256. rewriteFromPattern, err := regexp.Compile(rewriteFrom)
  257. if err != nil {
  258. return nil, fmt.Errorf("invalid regex matching pattern: %s", rewriteFrom)
  259. }
  260. if getSubExprUsage(rewriteTo) > rewriteFromPattern.NumSubexp() {
  261. return nil, fmt.Errorf("the rewrite regex pattern (%s) uses more subexpressions than its corresponding matching regex pattern (%s)", rewriteTo, rewriteFrom)
  262. }
  263. return rewriteFromPattern, nil
  264. }