| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | package middleware
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							|  |  |  | 	"strconv"
 | 
					
						
							|  |  |  | 	"strings"
 | 
					
						
							|  |  |  | 	"time"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"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
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewReplacer makes a new replacer based on r and rr.
 | 
					
						
							|  |  |  | // 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).
 | 
					
						
							|  |  |  | func NewReplacer(r *dns.Msg, rr *ResponseRecorder, emptyValue string) Replacer {
 | 
					
						
							| 
									
										
										
										
											2016-03-19 07:18:57 +00:00
										 |  |  | 	state := State{W: rr, Req: r}
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	rep := replacer{
 | 
					
						
							|  |  |  | 		replacements: map[string]string{
 | 
					
						
							| 
									
										
										
										
											2016-03-19 07:18:57 +00:00
										 |  |  | 			"{type}":  state.Type(),
 | 
					
						
							|  |  |  | 			"{name}":  state.Name(),
 | 
					
						
							|  |  |  | 			"{class}": state.Class(),
 | 
					
						
							|  |  |  | 			"{proto}": state.Proto(),
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 			"{when}": func() string {
 | 
					
						
							|  |  |  | 				return time.Now().Format(timeFormat)
 | 
					
						
							|  |  |  | 			}(),
 | 
					
						
							| 
									
										
										
										
											2016-03-19 07:18:57 +00:00
										 |  |  | 			"{remote}": state.IP(),
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 			"{port}": func() string {
 | 
					
						
							| 
									
										
										
										
											2016-03-19 07:18:57 +00:00
										 |  |  | 				p, _ := state.Port()
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 				return p
 | 
					
						
							|  |  |  | 			}(),
 | 
					
						
							|  |  |  | 		},
 | 
					
						
							|  |  |  | 		emptyValue: emptyValue,
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	if rr != nil {
 | 
					
						
							| 
									
										
										
										
											2016-03-19 20:17:44 +00:00
										 |  |  | 		rcode := dns.RcodeToString[rr.rcode]
 | 
					
						
							|  |  |  | 		if rcode == "" {
 | 
					
						
							|  |  |  | 			rcode = strconv.Itoa(rr.rcode)
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		rep.replacements["{rcode}"] = rcode
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 		rep.replacements["{size}"] = strconv.Itoa(rr.size)
 | 
					
						
							| 
									
										
										
										
											2016-04-03 17:05:16 +01:00
										 |  |  | 		rep.replacements["{duration}"] = time.Since(rr.start).String()
 | 
					
						
							| 
									
										
										
										
											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))
 | 
					
						
							|  |  |  | 	rep.replacements[headerReplacer+"opcode}"] = strconv.Itoa(int(r.Opcode))
 | 
					
						
							| 
									
										
										
										
											2016-04-03 17:16:46 +01:00
										 |  |  | 	rep.replacements[headerReplacer+"do}"] = boolToString(state.Do())
 | 
					
						
							| 
									
										
										
										
											2016-04-03 17:05:16 +01:00
										 |  |  | 	rep.replacements[headerReplacer+"bufsize}"] = strconv.Itoa(state.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"
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | const (
 | 
					
						
							|  |  |  | 	timeFormat     = "02/Jan/2006:15:04:05 -0700"
 | 
					
						
							|  |  |  | 	headerReplacer = "{>"
 | 
					
						
							|  |  |  | )
 |