mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-31 10:13:14 -04:00 
			
		
		
		
	* fix concurrent issue with cname rewrite plugin Signed-off-by: amila <amila.15@cse.mrt.ac.lk> * add nil check before deref, add AAAA type test case Signed-off-by: amila <amila.15@cse.mrt.ac.lk> --------- Signed-off-by: amila <amila.15@cse.mrt.ac.lk>
		
			
				
	
	
		
			153 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			153 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package rewrite
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"regexp"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/coredns/coredns/plugin/pkg/log"
 | |
| 	"github.com/coredns/coredns/plugin/pkg/upstream"
 | |
| 	"github.com/coredns/coredns/request"
 | |
| 
 | |
| 	"github.com/miekg/dns"
 | |
| )
 | |
| 
 | |
| // UpstreamInt wraps the Upstream API for dependency injection during testing
 | |
| type UpstreamInt interface {
 | |
| 	Lookup(ctx context.Context, state request.Request, name string, typ uint16) (*dns.Msg, error)
 | |
| }
 | |
| 
 | |
| // cnameTargetRule is cname target rewrite rule.
 | |
| type cnameTargetRule struct {
 | |
| 	rewriteType     string
 | |
| 	paramFromTarget string
 | |
| 	paramToTarget   string
 | |
| 	nextAction      string
 | |
| 	Upstream        UpstreamInt // Upstream for looking up external names during the resolution process.
 | |
| }
 | |
| 
 | |
| // cnameTargetRuleWithReqState is cname target rewrite rule state
 | |
| type cnameTargetRuleWithReqState struct {
 | |
| 	rule  cnameTargetRule
 | |
| 	state request.Request
 | |
| 	ctx   context.Context
 | |
| }
 | |
| 
 | |
| func (r *cnameTargetRule) getFromAndToTarget(inputCName string) (from string, to string) {
 | |
| 	switch r.rewriteType {
 | |
| 	case ExactMatch:
 | |
| 		return r.paramFromTarget, r.paramToTarget
 | |
| 	case PrefixMatch:
 | |
| 		if strings.HasPrefix(inputCName, r.paramFromTarget) {
 | |
| 			return inputCName, r.paramToTarget + strings.TrimPrefix(inputCName, r.paramFromTarget)
 | |
| 		}
 | |
| 	case SuffixMatch:
 | |
| 		if strings.HasSuffix(inputCName, r.paramFromTarget) {
 | |
| 			return inputCName, strings.TrimSuffix(inputCName, r.paramFromTarget) + r.paramToTarget
 | |
| 		}
 | |
| 	case SubstringMatch:
 | |
| 		if strings.Contains(inputCName, r.paramFromTarget) {
 | |
| 			return inputCName, strings.Replace(inputCName, r.paramFromTarget, r.paramToTarget, -1)
 | |
| 		}
 | |
| 	case RegexMatch:
 | |
| 		pattern := regexp.MustCompile(r.paramFromTarget)
 | |
| 		regexGroups := pattern.FindStringSubmatch(inputCName)
 | |
| 		if len(regexGroups) == 0 {
 | |
| 			return "", ""
 | |
| 		}
 | |
| 		substitution := r.paramToTarget
 | |
| 		for groupIndex, groupValue := range regexGroups {
 | |
| 			groupIndexStr := "{" + strconv.Itoa(groupIndex) + "}"
 | |
| 			substitution = strings.Replace(substitution, groupIndexStr, groupValue, -1)
 | |
| 		}
 | |
| 		return inputCName, substitution
 | |
| 	}
 | |
| 	return "", ""
 | |
| }
 | |
| 
 | |
| func (r *cnameTargetRuleWithReqState) RewriteResponse(res *dns.Msg, rr dns.RR) {
 | |
| 	// logic to rewrite the cname target of dns response
 | |
| 	switch rr.Header().Rrtype {
 | |
| 	case dns.TypeCNAME:
 | |
| 		// rename the target of the cname response
 | |
| 		if cname, ok := rr.(*dns.CNAME); ok {
 | |
| 			fromTarget, toTarget := r.rule.getFromAndToTarget(cname.Target)
 | |
| 			if cname.Target == fromTarget {
 | |
| 				// create upstream request with the new target with the same qtype
 | |
| 				r.state.Req.Question[0].Name = toTarget
 | |
| 				upRes, err := r.rule.Upstream.Lookup(r.ctx, r.state, toTarget, r.state.Req.Question[0].Qtype)
 | |
| 
 | |
| 				if err != nil {
 | |
| 					log.Errorf("Error upstream request %v", err)
 | |
| 				}
 | |
| 
 | |
| 				var newAnswer []dns.RR
 | |
| 				// iterate over first upstram response
 | |
| 				// add the cname record to the new answer
 | |
| 				for _, rr := range res.Answer {
 | |
| 					if cname, ok := rr.(*dns.CNAME); ok {
 | |
| 						// change the target name in the response
 | |
| 						cname.Target = toTarget
 | |
| 						newAnswer = append(newAnswer, rr)
 | |
| 					}
 | |
| 				}
 | |
| 				// iterate over upstream response received
 | |
| 				for _, rr := range upRes.Answer {
 | |
| 					if rr.Header().Name == toTarget {
 | |
| 						newAnswer = append(newAnswer, rr)
 | |
| 					}
 | |
| 				}
 | |
| 				res.Answer = newAnswer
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newCNAMERule(nextAction string, args ...string) (Rule, error) {
 | |
| 	var rewriteType string
 | |
| 	var paramFromTarget, paramToTarget string
 | |
| 	if len(args) == 3 {
 | |
| 		rewriteType = (strings.ToLower(args[0]))
 | |
| 		switch rewriteType {
 | |
| 		case ExactMatch:
 | |
| 		case PrefixMatch:
 | |
| 		case SuffixMatch:
 | |
| 		case SubstringMatch:
 | |
| 		case RegexMatch:
 | |
| 		default:
 | |
| 			return nil, fmt.Errorf("unknown cname rewrite type: %s", rewriteType)
 | |
| 		}
 | |
| 		paramFromTarget, paramToTarget = strings.ToLower(args[1]), strings.ToLower(args[2])
 | |
| 	} else if len(args) == 2 {
 | |
| 		rewriteType = ExactMatch
 | |
| 		paramFromTarget, paramToTarget = strings.ToLower(args[0]), strings.ToLower(args[1])
 | |
| 	} else {
 | |
| 		return nil, fmt.Errorf("too few (%d) arguments for a cname rule", len(args))
 | |
| 	}
 | |
| 	rule := cnameTargetRule{
 | |
| 		rewriteType:     rewriteType,
 | |
| 		paramFromTarget: paramFromTarget,
 | |
| 		paramToTarget:   paramToTarget,
 | |
| 		nextAction:      nextAction,
 | |
| 		Upstream:        upstream.New(),
 | |
| 	}
 | |
| 	return &rule, nil
 | |
| }
 | |
| 
 | |
| // Rewrite rewrites the current request.
 | |
| func (r *cnameTargetRule) Rewrite(ctx context.Context, state request.Request) (ResponseRules, Result) {
 | |
| 	if r != nil && len(r.rewriteType) > 0 && len(r.paramFromTarget) > 0 && len(r.paramToTarget) > 0 {
 | |
| 		return ResponseRules{&cnameTargetRuleWithReqState{
 | |
| 			rule:  *r,
 | |
| 			state: state,
 | |
| 			ctx:   ctx,
 | |
| 		}}, RewriteDone
 | |
| 	}
 | |
| 	return nil, RewriteIgnored
 | |
| }
 | |
| 
 | |
| // Mode returns the processing mode.
 | |
| func (r *cnameTargetRule) Mode() string { return r.nextAction }
 |