| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | package template
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							|  |  |  | 	"bytes"
 | 
					
						
							| 
									
										
										
										
											2018-04-22 08:34:35 +01:00
										 |  |  | 	"context"
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	"regexp"
 | 
					
						
							|  |  |  | 	"strconv"
 | 
					
						
							|  |  |  | 	gotmpl "text/template"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/coredns/coredns/plugin"
 | 
					
						
							| 
									
										
										
										
											2019-07-03 16:10:56 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin/metadata"
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:12 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin/metrics"
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin/pkg/fall"
 | 
					
						
							| 
									
										
										
										
											2018-02-16 03:45:25 -05:00
										 |  |  | 	"github.com/coredns/coredns/plugin/pkg/upstream"
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	"github.com/coredns/coredns/request"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/miekg/dns"
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Handler is a plugin handler that takes a query and templates a response.
 | 
					
						
							|  |  |  | type Handler struct {
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 	Zones []string
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	Next      plugin.Handler
 | 
					
						
							|  |  |  | 	Templates []template
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type template struct {
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 	zones      []string
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	rcode      int
 | 
					
						
							|  |  |  | 	regex      []*regexp.Regexp
 | 
					
						
							|  |  |  | 	answer     []*gotmpl.Template
 | 
					
						
							|  |  |  | 	additional []*gotmpl.Template
 | 
					
						
							|  |  |  | 	authority  []*gotmpl.Template
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 	qclass     uint16
 | 
					
						
							|  |  |  | 	qtype      uint16
 | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 	fall       fall.F
 | 
					
						
							| 
									
										
										
										
											2018-06-21 14:38:29 -04:00
										 |  |  | 	upstream   *upstream.Upstream
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type templateData struct {
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 	Zone     string
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	Name     string
 | 
					
						
							|  |  |  | 	Regex    string
 | 
					
						
							|  |  |  | 	Match    []string
 | 
					
						
							|  |  |  | 	Group    map[string]string
 | 
					
						
							|  |  |  | 	Class    string
 | 
					
						
							|  |  |  | 	Type     string
 | 
					
						
							|  |  |  | 	Message  *dns.Msg
 | 
					
						
							|  |  |  | 	Question *dns.Question
 | 
					
						
							| 
									
										
										
										
											2019-07-03 16:10:56 +01:00
										 |  |  | 	md       map[string]metadata.Func
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (data *templateData) Meta(metaName string) string {
 | 
					
						
							|  |  |  | 	if data.md == nil {
 | 
					
						
							|  |  |  | 		return ""
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if f, ok := data.md[metaName]; ok {
 | 
					
						
							|  |  |  | 		return f()
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ""
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServeDNS implements the plugin.Handler interface.
 | 
					
						
							|  |  |  | func (h Handler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
 | 
					
						
							| 
									
										
										
										
											2019-03-26 14:37:30 +00:00
										 |  |  | 	state := request.Request{W: w, Req: r}
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 	zone := plugin.Zones(h.Zones).Matches(state.Name())
 | 
					
						
							|  |  |  | 	if zone == "" {
 | 
					
						
							|  |  |  | 		return plugin.NextOrFailure(h.Name(), h.Next, ctx, w, r)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	for _, template := range h.Templates {
 | 
					
						
							| 
									
										
										
										
											2019-07-03 16:10:56 +01:00
										 |  |  | 		data, match, fthrough := template.match(ctx, state, zone)
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 		if !match {
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 			if !fthrough {
 | 
					
						
							|  |  |  | 				return dns.RcodeNameError, nil
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 			continue
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:12 +01:00
										 |  |  | 		templateMatchesCount.WithLabelValues(metrics.WithServer(ctx), data.Zone, data.Class, data.Type).Inc()
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if template.rcode == dns.RcodeServerFailure {
 | 
					
						
							|  |  |  | 			return template.rcode, nil
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		msg := new(dns.Msg)
 | 
					
						
							|  |  |  | 		msg.SetReply(r)
 | 
					
						
							| 
									
										
										
										
											2018-12-30 17:05:08 +01:00
										 |  |  | 		msg.Authoritative = true
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 		msg.Rcode = template.rcode
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for _, answer := range template.answer {
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:12 +01:00
										 |  |  | 			rr, err := executeRRTemplate(metrics.WithServer(ctx), "answer", answer, data)
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 			if err != nil {
 | 
					
						
							|  |  |  | 				return dns.RcodeServerFailure, err
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			msg.Answer = append(msg.Answer, rr)
 | 
					
						
							| 
									
										
										
										
											2018-06-21 14:38:29 -04:00
										 |  |  | 			if template.upstream != nil && (state.QType() == dns.TypeA || state.QType() == dns.TypeAAAA) && rr.Header().Rrtype == dns.TypeCNAME {
 | 
					
						
							| 
									
										
										
										
											2019-03-26 14:37:30 +00:00
										 |  |  | 				up, _ := template.upstream.Lookup(ctx, state, rr.(*dns.CNAME).Target, state.QType())
 | 
					
						
							| 
									
										
										
										
											2018-02-16 03:45:25 -05:00
										 |  |  | 				msg.Answer = append(msg.Answer, up.Answer...)
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 		}
 | 
					
						
							|  |  |  | 		for _, additional := range template.additional {
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:12 +01:00
										 |  |  | 			rr, err := executeRRTemplate(metrics.WithServer(ctx), "additional", additional, data)
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 			if err != nil {
 | 
					
						
							|  |  |  | 				return dns.RcodeServerFailure, err
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			msg.Extra = append(msg.Extra, rr)
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		for _, authority := range template.authority {
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:12 +01:00
										 |  |  | 			rr, err := executeRRTemplate(metrics.WithServer(ctx), "authority", authority, data)
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 			if err != nil {
 | 
					
						
							|  |  |  | 				return dns.RcodeServerFailure, err
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			msg.Ns = append(msg.Ns, rr)
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		w.WriteMsg(msg)
 | 
					
						
							|  |  |  | 		return template.rcode, nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return h.Next.ServeDNS(ctx, w, r)
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Name implements the plugin.Handler interface.
 | 
					
						
							| 
									
										
										
										
											2018-01-08 13:13:25 +00:00
										 |  |  | func (h Handler) Name() string { return "template" }
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 16:10:56 +01:00
										 |  |  | func executeRRTemplate(server, section string, template *gotmpl.Template, data *templateData) (dns.RR, error) {
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	buffer := &bytes.Buffer{}
 | 
					
						
							|  |  |  | 	err := template.Execute(buffer, data)
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:12 +01:00
										 |  |  | 		templateFailureCount.WithLabelValues(server, data.Zone, data.Class, data.Type, section, template.Tree.Root.String()).Inc()
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 		return nil, err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	rr, err := dns.NewRR(buffer.String())
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:12 +01:00
										 |  |  | 		templateRRFailureCount.WithLabelValues(server, data.Zone, data.Class, data.Type, section, template.Tree.Root.String()).Inc()
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 		return rr, err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return rr, nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 16:10:56 +01:00
										 |  |  | func (t template) match(ctx context.Context, state request.Request, zone string) (*templateData, bool, bool) {
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	q := state.Req.Question[0]
 | 
					
						
							| 
									
										
										
										
											2019-07-03 16:10:56 +01:00
										 |  |  | 	data := &templateData{md: metadata.ValueFuncs(ctx)}
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 	zone = plugin.Zones(t.zones).Matches(state.Name())
 | 
					
						
							|  |  |  | 	if zone == "" {
 | 
					
						
							|  |  |  | 		return data, false, true
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if t.qclass != dns.ClassANY && q.Qclass != dns.ClassANY && q.Qclass != t.qclass {
 | 
					
						
							|  |  |  | 		return data, false, true
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | 	if t.qtype != dns.TypeANY && q.Qtype != dns.TypeANY && q.Qtype != t.qtype {
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		return data, false, true
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	for _, regex := range t.regex {
 | 
					
						
							|  |  |  | 		if !regex.MatchString(state.Name()) {
 | 
					
						
							|  |  |  | 			continue
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		data.Zone = zone
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 		data.Regex = regex.String()
 | 
					
						
							|  |  |  | 		data.Name = state.Name()
 | 
					
						
							|  |  |  | 		data.Question = &q
 | 
					
						
							|  |  |  | 		data.Message = state.Req
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		if q.Qclass != dns.ClassANY {
 | 
					
						
							|  |  |  | 			data.Class = dns.ClassToString[q.Qclass]
 | 
					
						
							|  |  |  | 		} else {
 | 
					
						
							|  |  |  | 			data.Class = dns.ClassToString[t.qclass]
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		if q.Qtype != dns.TypeANY {
 | 
					
						
							|  |  |  | 			data.Type = dns.TypeToString[q.Qtype]
 | 
					
						
							|  |  |  | 		} else {
 | 
					
						
							|  |  |  | 			data.Type = dns.TypeToString[t.qtype]
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		matches := regex.FindStringSubmatch(state.Name())
 | 
					
						
							|  |  |  | 		data.Match = make([]string, len(matches))
 | 
					
						
							|  |  |  | 		data.Group = make(map[string]string)
 | 
					
						
							|  |  |  | 		groupNames := regex.SubexpNames()
 | 
					
						
							|  |  |  | 		for i, m := range matches {
 | 
					
						
							|  |  |  | 			data.Match[i] = m
 | 
					
						
							|  |  |  | 			data.Group[strconv.Itoa(i)] = m
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		for i, m := range matches {
 | 
					
						
							|  |  |  | 			if len(groupNames[i]) > 0 {
 | 
					
						
							|  |  |  | 				data.Group[groupNames[i]] = m
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 		return data, true, false
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2018-01-09 22:30:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-09 21:48:32 +00:00
										 |  |  | 	return data, false, t.fall.Through(state.Name())
 | 
					
						
							| 
									
										
										
										
											2018-01-08 11:52:25 +01:00
										 |  |  | }
 |