package dns import ( "context" "errors" "net" "time" "github.com/miekg/dns" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) // ListenAndServe calls dns.ListenAndServe with a wrapped Handler. func ListenAndServe(addr string, network string, handler dns.Handler) error { return dns.ListenAndServe(addr, network, WrapHandler(handler)) } // ListenAndServeTLS calls dns.ListenAndServeTLS with a wrapped Handler. func ListenAndServeTLS(addr, certFile, keyFile string, handler dns.Handler) error { return dns.ListenAndServeTLS(addr, certFile, keyFile, WrapHandler(handler)) } // A Handler wraps a DNS Handler so that requests are traced. type Handler struct { dns.Handler } // WrapHandler creates a new, wrapped DNS handler. func WrapHandler(handler dns.Handler) *Handler { return &Handler{ Handler: handler, } } // ServeDNS dispatches requests to the underlying Handler. All requests will be // traced. func (h *Handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { span, _ := startSpan(context.Background(), r.Opcode) rw := &responseWriter{ResponseWriter: w} h.Handler.ServeDNS(rw, r) span.Finish(tracer.WithError(rw.err)) } type responseWriter struct { dns.ResponseWriter err error } // WriteMsg writes the message to the response writer. If a non-success rcode is // set the error in the struct will be non-nil. func (rw *responseWriter) WriteMsg(msg *dns.Msg) error { if msg.Rcode != dns.RcodeSuccess { rw.err = errors.New(dns.RcodeToString[msg.Rcode]) } return rw.ResponseWriter.WriteMsg(msg) } // Exchange calls dns.Exchange and traces the request. func Exchange(m *dns.Msg, addr string) (r *dns.Msg, err error) { span, _ := startSpan(context.Background(), m.Opcode) r, err = dns.Exchange(m, addr) span.Finish(tracer.WithError(err)) return r, err } // ExchangeConn calls dns.ExchangeConn and traces the request. func ExchangeConn(c net.Conn, m *dns.Msg) (r *dns.Msg, err error) { span, _ := startSpan(context.Background(), m.Opcode) r, err = dns.ExchangeConn(c, m) span.Finish(tracer.WithError(err)) return r, err } // ExchangeContext calls dns.ExchangeContext and traces the request. func ExchangeContext(ctx context.Context, m *dns.Msg, addr string) (r *dns.Msg, err error) { span, ctx := startSpan(ctx, m.Opcode) r, err = dns.ExchangeContext(ctx, m, addr) span.Finish(tracer.WithError(err)) return r, err } // A Client wraps a DNS Client so that requests are traced. type Client struct { *dns.Client } // Exchange calls the underlying Client.Exchange and traces the request. func (c *Client) Exchange(m *dns.Msg, addr string) (r *dns.Msg, rtt time.Duration, err error) { span, _ := startSpan(context.Background(), m.Opcode) r, rtt, err = c.Client.Exchange(m, addr) span.Finish(tracer.WithError(err)) return r, rtt, err } // ExchangeContext calls the underlying Client.ExchangeContext and traces the request. func (c *Client) ExchangeContext(ctx context.Context, m *dns.Msg, addr string) (r *dns.Msg, rtt time.Duration, err error) { span, ctx := startSpan(ctx, m.Opcode) r, rtt, err = c.Client.ExchangeContext(ctx, m, addr) span.Finish(tracer.WithError(err)) return r, rtt, err } func startSpan(ctx context.Context, opcode int) (ddtrace.Span, context.Context) { return tracer.StartSpanFromContext(ctx, "dns.request", tracer.ServiceName("dns"), tracer.ResourceName(dns.OpcodeToString[opcode]), tracer.SpanType(ext.SpanTypeDNS)) }