| 
									
										
										
										
											2023-04-13 17:49:36 +05:30
										 |  |  | 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.
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-09 18:23:52 +05:30
										 |  |  | // cnameTargetRuleWithReqState is cname target rewrite rule state
 | 
					
						
							|  |  |  | type cnameTargetRuleWithReqState struct {
 | 
					
						
							|  |  |  | 	rule  cnameTargetRule
 | 
					
						
							|  |  |  | 	state request.Request
 | 
					
						
							|  |  |  | 	ctx   context.Context
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-13 17:49:36 +05:30
										 |  |  | 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 "", ""
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-09 18:23:52 +05:30
										 |  |  | func (r *cnameTargetRuleWithReqState) RewriteResponse(res *dns.Msg, rr dns.RR) {
 | 
					
						
							| 
									
										
										
										
											2023-04-13 17:49:36 +05:30
										 |  |  | 	// 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 {
 | 
					
						
							| 
									
										
										
										
											2023-12-09 18:23:52 +05:30
										 |  |  | 			fromTarget, toTarget := r.rule.getFromAndToTarget(cname.Target)
 | 
					
						
							| 
									
										
										
										
											2023-04-13 17:49:36 +05:30
										 |  |  | 			if cname.Target == fromTarget {
 | 
					
						
							|  |  |  | 				// create upstream request with the new target with the same qtype
 | 
					
						
							|  |  |  | 				r.state.Req.Question[0].Name = toTarget
 | 
					
						
							| 
									
										
										
										
											2023-12-09 18:23:52 +05:30
										 |  |  | 				upRes, err := r.rule.Upstream.Lookup(r.ctx, r.state, toTarget, r.state.Req.Question[0].Qtype)
 | 
					
						
							| 
									
										
										
										
											2023-04-13 17:49:36 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | 				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)
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2023-08-10 23:06:48 +08:00
										 |  |  | 				// iterate over upstream response received
 | 
					
						
							| 
									
										
										
										
											2023-04-13 17:49:36 +05:30
										 |  |  | 				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) {
 | 
					
						
							| 
									
										
										
										
											2023-12-09 18:23:52 +05:30
										 |  |  | 	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
 | 
					
						
							| 
									
										
										
										
											2023-04-13 17:49:36 +05:30
										 |  |  | 	}
 | 
					
						
							|  |  |  | 	return nil, RewriteIgnored
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Mode returns the processing mode.
 | 
					
						
							|  |  |  | func (r *cnameTargetRule) Mode() string { return r.nextAction }
 |