| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | package replacer | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-21 15:15:47 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin/pkg/dnstest" | 
					
						
							| 
									
										
										
										
											2017-02-21 22:51:47 -08:00
										 |  |  | 	"github.com/coredns/coredns/request" | 
					
						
							| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	"github.com/miekg/dns" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Replacer is a type which can replace placeholder | 
					
						
							|  |  |  | // substrings in a string with actual values from a | 
					
						
							| 
									
										
										
										
											2016-04-04 09:10:07 +00:00
										 |  |  | // dns.Msg and responseRecorder. Always use | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | // NewReplacer to get one of these. | 
					
						
							|  |  |  | type Replacer interface { | 
					
						
							|  |  |  | 	Replace(string) string | 
					
						
							|  |  |  | 	Set(key, value string) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type replacer struct { | 
					
						
							|  |  |  | 	replacements map[string]string | 
					
						
							|  |  |  | 	emptyValue   string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | // New makes a new replacer based on r and rr. | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | // Do not create a new replacer until r and rr have all | 
					
						
							|  |  |  | // the needed values, because this function copies those | 
					
						
							|  |  |  | // values into the replacer. rr may be nil if it is not | 
					
						
							|  |  |  | // available. emptyValue should be the string that is used | 
					
						
							|  |  |  | // in place of empty string (can still be empty string). | 
					
						
							| 
									
										
										
										
											2017-09-21 15:15:47 +01:00
										 |  |  | func New(r *dns.Msg, rr *dnstest.Recorder, emptyValue string) Replacer { | 
					
						
							| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | 	req := request.Request{W: rr, Req: r} | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	rep := replacer{ | 
					
						
							|  |  |  | 		replacements: map[string]string{ | 
					
						
							| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | 			"{type}":  req.Type(), | 
					
						
							|  |  |  | 			"{name}":  req.Name(), | 
					
						
							|  |  |  | 			"{class}": req.Class(), | 
					
						
							|  |  |  | 			"{proto}": req.Proto(), | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 			"{when}": func() string { | 
					
						
							|  |  |  | 				return time.Now().Format(timeFormat) | 
					
						
							|  |  |  | 			}(), | 
					
						
							| 
									
										
										
										
											2016-11-30 20:44:00 +00:00
										 |  |  | 			"{size}":   strconv.Itoa(req.Len()), | 
					
						
							| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | 			"{remote}": req.IP(), | 
					
						
							|  |  |  | 			"{port}":   req.Port(), | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		emptyValue: emptyValue, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if rr != nil { | 
					
						
							| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | 		rcode := dns.RcodeToString[rr.Rcode] | 
					
						
							| 
									
										
										
										
											2016-03-19 20:17:44 +00:00
										 |  |  | 		if rcode == "" { | 
					
						
							| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | 			rcode = strconv.Itoa(rr.Rcode) | 
					
						
							| 
									
										
										
										
											2016-03-19 20:17:44 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		rep.replacements["{rcode}"] = rcode | 
					
						
							| 
									
										
										
										
											2016-11-30 20:44:00 +00:00
										 |  |  | 		rep.replacements["{rsize}"] = strconv.Itoa(rr.Len) | 
					
						
							| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | 		rep.replacements["{duration}"] = time.Since(rr.Start).String() | 
					
						
							| 
									
										
										
										
											2017-08-07 03:49:40 -07:00
										 |  |  | 		if rr.Msg != nil { | 
					
						
							|  |  |  | 			rep.replacements[headerReplacer+"rflags}"] = flagsToString(rr.Msg.MsgHdr) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-19 11:16:08 +00:00
										 |  |  | 	// Header placeholders (case-insensitive) | 
					
						
							|  |  |  | 	rep.replacements[headerReplacer+"id}"] = strconv.Itoa(int(r.Id)) | 
					
						
							| 
									
										
										
										
											2017-08-06 05:54:24 -07:00
										 |  |  | 	rep.replacements[headerReplacer+"opcode}"] = strconv.Itoa(r.Opcode) | 
					
						
							| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | 	rep.replacements[headerReplacer+"do}"] = boolToString(req.Do()) | 
					
						
							|  |  |  | 	rep.replacements[headerReplacer+"bufsize}"] = strconv.Itoa(req.Size()) | 
					
						
							| 
									
										
										
										
											2016-03-19 11:16:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	return rep | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Replace performs a replacement of values on s and returns | 
					
						
							|  |  |  | // the string with the replaced values. | 
					
						
							|  |  |  | func (r replacer) Replace(s string) string { | 
					
						
							|  |  |  | 	// Header replacements - these are case-insensitive, so we can't just use strings.Replace() | 
					
						
							|  |  |  | 	for strings.Contains(s, headerReplacer) { | 
					
						
							|  |  |  | 		idxStart := strings.Index(s, headerReplacer) | 
					
						
							|  |  |  | 		endOffset := idxStart + len(headerReplacer) | 
					
						
							|  |  |  | 		idxEnd := strings.Index(s[endOffset:], "}") | 
					
						
							|  |  |  | 		if idxEnd > -1 { | 
					
						
							|  |  |  | 			placeholder := strings.ToLower(s[idxStart : endOffset+idxEnd+1]) | 
					
						
							|  |  |  | 			replacement := r.replacements[placeholder] | 
					
						
							|  |  |  | 			if replacement == "" { | 
					
						
							|  |  |  | 				replacement = r.emptyValue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			s = s[:idxStart] + replacement + s[endOffset+idxEnd+1:] | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Regular replacements - these are easier because they're case-sensitive | 
					
						
							|  |  |  | 	for placeholder, replacement := range r.replacements { | 
					
						
							|  |  |  | 		if replacement == "" { | 
					
						
							|  |  |  | 			replacement = r.emptyValue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		s = strings.Replace(s, placeholder, replacement, -1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return s | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Set sets key to value in the replacements map. | 
					
						
							|  |  |  | func (r replacer) Set(key, value string) { | 
					
						
							|  |  |  | 	r.replacements["{"+key+"}"] = value | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-03 17:16:46 +01:00
										 |  |  | func boolToString(b bool) string { | 
					
						
							|  |  |  | 	if b { | 
					
						
							|  |  |  | 		return "true" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "false" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-07 03:49:40 -07:00
										 |  |  | // flagsToString checks all header flags and returns those | 
					
						
							|  |  |  | // that are set as a string separated with commas | 
					
						
							|  |  |  | func flagsToString(h dns.MsgHdr) string { | 
					
						
							|  |  |  | 	flags := make([]string, 7) | 
					
						
							|  |  |  | 	i := 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if h.Response { | 
					
						
							|  |  |  | 		flags[i] = "qr" | 
					
						
							|  |  |  | 		i++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if h.Authoritative { | 
					
						
							|  |  |  | 		flags[i] = "aa" | 
					
						
							|  |  |  | 		i++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if h.Truncated { | 
					
						
							|  |  |  | 		flags[i] = "tc" | 
					
						
							|  |  |  | 		i++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if h.RecursionDesired { | 
					
						
							|  |  |  | 		flags[i] = "rd" | 
					
						
							|  |  |  | 		i++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if h.RecursionAvailable { | 
					
						
							|  |  |  | 		flags[i] = "ra" | 
					
						
							|  |  |  | 		i++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if h.Zero { | 
					
						
							|  |  |  | 		flags[i] = "z" | 
					
						
							|  |  |  | 		i++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if h.AuthenticatedData { | 
					
						
							|  |  |  | 		flags[i] = "ad" | 
					
						
							|  |  |  | 		i++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if h.CheckingDisabled { | 
					
						
							|  |  |  | 		flags[i] = "cd" | 
					
						
							|  |  |  | 		i++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return strings.Join(flags[:i], ",") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	timeFormat     = "02/Jan/2006:15:04:05 -0700" | 
					
						
							|  |  |  | 	headerReplacer = "{>" | 
					
						
							|  |  |  | ) |