package realm import ( "fmt" "time" "github.com/DataDog/datadog-go/statsd" "github.com/miekg/dns" ) // A Server listens for DNS requests over UDP and responds with answers from the provided Zone. type Server struct { server *dns.Server registry *Registry statsd *statsd.Client } // NewServer returns a new initialized *Server that will bind to listen and will look up answers from zone. func NewServer(listen string, registry *Registry, statsdHost string) (*Server, error) { var err error var s *Server s = &Server{registry: registry} s.server = &dns.Server{ Addr: listen, Net: "udp", Handler: s, } if statsdHost != "" { s.statsd, err = statsd.New(statsdHost) if err != nil { return nil, err } s.statsd.Namespace = "realm." } return s, err } // ListenAndServe will start the nameserver on the configured address. func (s *Server) ListenAndServe() error { return s.server.ListenAndServe() } // ServeDNS will be called for every DNS request to this server. // It will attempt to provide answers to all questions from the configured zone. func (s *Server) ServeDNS(w dns.ResponseWriter, request *dns.Msg) { // Capture starting time for measuring message response time var start time.Time start = time.Now() // Setup the default response var response *dns.Msg response = &dns.Msg{} response.SetReply(request) response.Compress = true // Lookup answers to any of the questions for _, question := range request.Question { // Capture starting time for measuring lookup var lookupStart time.Time lookupStart = time.Now() // Perform lookup for this question var records []dns.RR records = s.registry.Lookup(question.Name, question.Qtype, question.Qclass) // Capture ending and elapsed time var lookupElapsed time.Duration lookupElapsed = time.Since(lookupStart) // Append results to the response response.Answer = append(response.Answer, records...) // If StatsD is enabled, record some metrics if s.statsd != nil { var tags []string tags = []string{ fmt.Sprintf("name:%s", question.Name), fmt.Sprintf("qtype:%s", dns.TypeToString[question.Qtype]), fmt.Sprintf("qclass:%s", dns.ClassToString[question.Qclass]), } s.statsd.TimeInMilliseconds("lookup.time", lookupElapsed.Seconds()*1000.0, tags, 1) s.statsd.Histogram("lookup.answer", float64(len(records)), tags, 1) s.statsd.Count("request.question", 1, tags, 1) } } // Respond to the request w.WriteMsg(response) // Record any ending metrics if s.statsd != nil { var elapsed time.Duration elapsed = time.Since(start) s.statsd.TimeInMilliseconds("request.time", elapsed.Seconds()*1000.0, nil, 1) } }