2016-02-21 17:43:24 +00:00
|
|
|
package dns_query
|
2016-02-13 18:00:42 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"github.com/miekg/dns"
|
|
|
|
"net"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
2016-07-19 13:03:28 +00:00
|
|
|
|
|
|
|
"github.com/influxdata/telegraf"
|
|
|
|
"github.com/influxdata/telegraf/internal/errchan"
|
2017-02-04 15:58:02 +00:00
|
|
|
"github.com/influxdata/telegraf/registry/inputs"
|
2016-02-13 18:00:42 +00:00
|
|
|
)
|
|
|
|
|
2016-02-21 17:43:24 +00:00
|
|
|
type DnsQuery struct {
|
2016-02-13 18:00:42 +00:00
|
|
|
// Domains or subdomains to query
|
|
|
|
Domains []string
|
|
|
|
|
|
|
|
// Server to query
|
|
|
|
Servers []string
|
|
|
|
|
|
|
|
// Record type
|
2016-02-15 15:10:04 +00:00
|
|
|
RecordType string `toml:"record_type"`
|
2016-02-13 18:00:42 +00:00
|
|
|
|
|
|
|
// DNS server port number
|
|
|
|
Port int
|
|
|
|
|
|
|
|
// Dns query timeout in seconds. 0 means no timeout
|
|
|
|
Timeout int
|
|
|
|
}
|
|
|
|
|
|
|
|
var sampleConfig = `
|
2016-02-21 17:43:24 +00:00
|
|
|
## servers to query
|
2016-02-13 18:00:42 +00:00
|
|
|
servers = ["8.8.8.8"] # required
|
|
|
|
|
2016-02-21 17:43:24 +00:00
|
|
|
## Domains or subdomains to query. "."(root) is default
|
|
|
|
domains = ["."] # optional
|
|
|
|
|
2016-03-31 23:50:24 +00:00
|
|
|
## Query record type. Default is "A"
|
|
|
|
## Posible values: A, AAAA, CNAME, MX, NS, PTR, TXT, SOA, SPF, SRV.
|
2016-02-15 15:10:04 +00:00
|
|
|
record_type = "A" # optional
|
2016-02-13 18:00:42 +00:00
|
|
|
|
2016-02-21 17:43:24 +00:00
|
|
|
## Dns server port. 53 is default
|
2016-02-13 18:00:42 +00:00
|
|
|
port = 53 # optional
|
|
|
|
|
2016-02-21 17:43:24 +00:00
|
|
|
## Query timeout in seconds. Default is 2 seconds
|
2016-02-13 18:00:42 +00:00
|
|
|
timeout = 2 # optional
|
|
|
|
`
|
|
|
|
|
2016-02-21 17:43:24 +00:00
|
|
|
func (d *DnsQuery) SampleConfig() string {
|
2016-02-13 18:00:42 +00:00
|
|
|
return sampleConfig
|
|
|
|
}
|
|
|
|
|
2016-02-21 17:43:24 +00:00
|
|
|
func (d *DnsQuery) Description() string {
|
2016-02-13 18:00:42 +00:00
|
|
|
return "Query given DNS server and gives statistics"
|
|
|
|
}
|
2016-02-21 17:43:24 +00:00
|
|
|
func (d *DnsQuery) Gather(acc telegraf.Accumulator) error {
|
2016-02-13 18:00:42 +00:00
|
|
|
d.setDefaultValues()
|
2016-07-19 13:03:28 +00:00
|
|
|
|
|
|
|
errChan := errchan.New(len(d.Domains) * len(d.Servers))
|
2016-02-13 18:00:42 +00:00
|
|
|
for _, domain := range d.Domains {
|
|
|
|
for _, server := range d.Servers {
|
|
|
|
dnsQueryTime, err := d.getDnsQueryTime(domain, server)
|
2016-07-19 13:03:28 +00:00
|
|
|
errChan.C <- err
|
2016-02-13 18:00:42 +00:00
|
|
|
tags := map[string]string{
|
2016-02-15 15:10:04 +00:00
|
|
|
"server": server,
|
|
|
|
"domain": domain,
|
|
|
|
"record_type": d.RecordType,
|
2016-02-13 18:00:42 +00:00
|
|
|
}
|
|
|
|
|
2016-02-15 15:10:04 +00:00
|
|
|
fields := map[string]interface{}{"query_time_ms": dnsQueryTime}
|
2016-02-21 17:43:24 +00:00
|
|
|
acc.AddFields("dns_query", fields, tags)
|
2016-02-13 18:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-19 13:03:28 +00:00
|
|
|
return errChan.Error()
|
2016-02-13 18:00:42 +00:00
|
|
|
}
|
|
|
|
|
2016-02-21 17:43:24 +00:00
|
|
|
func (d *DnsQuery) setDefaultValues() {
|
2016-02-13 18:00:42 +00:00
|
|
|
if len(d.RecordType) == 0 {
|
2016-02-21 17:43:24 +00:00
|
|
|
d.RecordType = "NS"
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(d.Domains) == 0 {
|
|
|
|
d.Domains = []string{"."}
|
|
|
|
d.RecordType = "NS"
|
2016-02-13 18:00:42 +00:00
|
|
|
}
|
2016-02-21 17:43:24 +00:00
|
|
|
|
2016-02-13 18:00:42 +00:00
|
|
|
if d.Port == 0 {
|
|
|
|
d.Port = 53
|
|
|
|
}
|
2016-02-21 17:43:24 +00:00
|
|
|
|
2016-02-13 18:00:42 +00:00
|
|
|
if d.Timeout == 0 {
|
|
|
|
d.Timeout = 2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-21 17:43:24 +00:00
|
|
|
func (d *DnsQuery) getDnsQueryTime(domain string, server string) (float64, error) {
|
2016-02-13 18:00:42 +00:00
|
|
|
dnsQueryTime := float64(0)
|
|
|
|
|
|
|
|
c := new(dns.Client)
|
|
|
|
c.ReadTimeout = time.Duration(d.Timeout) * time.Second
|
|
|
|
|
|
|
|
m := new(dns.Msg)
|
|
|
|
recordType, err := d.parseRecordType()
|
|
|
|
if err != nil {
|
|
|
|
return dnsQueryTime, err
|
|
|
|
}
|
|
|
|
m.SetQuestion(dns.Fqdn(domain), recordType)
|
|
|
|
m.RecursionDesired = true
|
|
|
|
|
2016-02-15 15:10:04 +00:00
|
|
|
r, rtt, err := c.Exchange(m, net.JoinHostPort(server, strconv.Itoa(d.Port)))
|
2016-02-13 18:00:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return dnsQueryTime, err
|
|
|
|
}
|
|
|
|
if r.Rcode != dns.RcodeSuccess {
|
|
|
|
return dnsQueryTime, errors.New(fmt.Sprintf("Invalid answer name %s after %s query for %s\n", domain, d.RecordType, domain))
|
|
|
|
}
|
2016-02-15 15:10:04 +00:00
|
|
|
dnsQueryTime = float64(rtt.Nanoseconds()) / 1e6
|
2016-02-13 18:00:42 +00:00
|
|
|
return dnsQueryTime, nil
|
|
|
|
}
|
|
|
|
|
2016-02-21 17:43:24 +00:00
|
|
|
func (d *DnsQuery) parseRecordType() (uint16, error) {
|
2016-02-13 18:00:42 +00:00
|
|
|
var recordType uint16
|
|
|
|
var error error
|
|
|
|
|
|
|
|
switch d.RecordType {
|
|
|
|
case "A":
|
|
|
|
recordType = dns.TypeA
|
2016-02-15 15:10:04 +00:00
|
|
|
case "AAAA":
|
|
|
|
recordType = dns.TypeAAAA
|
|
|
|
case "ANY":
|
|
|
|
recordType = dns.TypeANY
|
2016-02-13 18:00:42 +00:00
|
|
|
case "CNAME":
|
|
|
|
recordType = dns.TypeCNAME
|
|
|
|
case "MX":
|
|
|
|
recordType = dns.TypeMX
|
|
|
|
case "NS":
|
|
|
|
recordType = dns.TypeNS
|
2016-02-21 17:43:24 +00:00
|
|
|
case "PTR":
|
|
|
|
recordType = dns.TypePTR
|
|
|
|
case "SOA":
|
|
|
|
recordType = dns.TypeSOA
|
|
|
|
case "SPF":
|
|
|
|
recordType = dns.TypeSPF
|
|
|
|
case "SRV":
|
|
|
|
recordType = dns.TypeSRV
|
2016-02-13 18:00:42 +00:00
|
|
|
case "TXT":
|
|
|
|
recordType = dns.TypeTXT
|
|
|
|
default:
|
|
|
|
error = errors.New(fmt.Sprintf("Record type %s not recognized", d.RecordType))
|
|
|
|
}
|
|
|
|
|
|
|
|
return recordType, error
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
2016-02-15 15:10:04 +00:00
|
|
|
inputs.Add("dns_query", func() telegraf.Input {
|
2016-02-21 17:43:24 +00:00
|
|
|
return &DnsQuery{}
|
2016-02-13 18:00:42 +00:00
|
|
|
})
|
|
|
|
}
|