telegraf/plugins/inputs/dns_query/dns_query.go

217 lines
4.2 KiB
Go
Raw Normal View History

package dns_query
2016-02-13 18:00:42 +00:00
import (
"errors"
"fmt"
"net"
"strconv"
"sync"
2016-02-13 18:00:42 +00:00
"time"
"github.com/miekg/dns"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
2016-02-13 18:00:42 +00:00
)
type ResultType uint64
const (
Success ResultType = 0
Timeout = 1
Error = 2
)
type DnsQuery struct {
2016-02-13 18:00:42 +00:00
// Domains or subdomains to query
Domains []string
2017-11-01 00:00:06 +00:00
// Network protocol name
Network string
2016-02-13 18:00:42 +00:00
// Server to query
Servers []string
// Record type
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 = `
## servers to query
servers = ["8.8.8.8"]
2016-02-13 18:00:42 +00:00
## Network is the network protocol name.
# network = "udp"
## Domains or subdomains to query.
# domains = ["."]
## Query record type.
## Posible values: A, AAAA, CNAME, MX, NS, PTR, TXT, SOA, SPF, SRV.
# record_type = "A"
2016-02-13 18:00:42 +00:00
## Dns server port.
# port = 53
2016-02-13 18:00:42 +00:00
## Query timeout in seconds.
# timeout = 2
2016-02-13 18:00:42 +00:00
`
func (d *DnsQuery) SampleConfig() string {
2016-02-13 18:00:42 +00:00
return sampleConfig
}
func (d *DnsQuery) Description() string {
2016-02-13 18:00:42 +00:00
return "Query given DNS server and gives statistics"
}
func (d *DnsQuery) Gather(acc telegraf.Accumulator) error {
var wg sync.WaitGroup
2016-02-13 18:00:42 +00:00
d.setDefaultValues()
2016-02-13 18:00:42 +00:00
for _, domain := range d.Domains {
for _, server := range d.Servers {
wg.Add(1)
go func(domain, server string) {
fields := make(map[string]interface{}, 2)
tags := map[string]string{
"server": server,
"domain": domain,
"record_type": d.RecordType,
}
dnsQueryTime, rcode, err := d.getDnsQueryTime(domain, server)
if rcode >= 0 {
tags["rcode"] = dns.RcodeToString[rcode]
fields["rcode_value"] = rcode
}
if err == nil {
setResult(Success, fields, tags)
fields["query_time_ms"] = dnsQueryTime
} else if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
setResult(Timeout, fields, tags)
} else if err != nil {
setResult(Error, fields, tags)
acc.AddError(err)
}
acc.AddFields("dns_query", fields, tags)
2018-10-11 01:29:33 +00:00
wg.Done()
}(domain, server)
2016-02-13 18:00:42 +00:00
}
}
wg.Wait()
2017-04-24 18:13:26 +00:00
return nil
2016-02-13 18:00:42 +00:00
}
func (d *DnsQuery) setDefaultValues() {
if d.Network == "" {
d.Network = "udp"
}
2016-02-13 18:00:42 +00:00
if len(d.RecordType) == 0 {
d.RecordType = "NS"
}
if len(d.Domains) == 0 {
d.Domains = []string{"."}
d.RecordType = "NS"
2016-02-13 18:00:42 +00:00
}
2016-02-13 18:00:42 +00:00
if d.Port == 0 {
d.Port = 53
}
2016-02-13 18:00:42 +00:00
if d.Timeout == 0 {
d.Timeout = 2
}
}
func (d *DnsQuery) getDnsQueryTime(domain string, server string) (float64, int, error) {
2016-02-13 18:00:42 +00:00
dnsQueryTime := float64(0)
c := new(dns.Client)
c.ReadTimeout = time.Duration(d.Timeout) * time.Second
c.Net = d.Network
2016-02-13 18:00:42 +00:00
m := new(dns.Msg)
recordType, err := d.parseRecordType()
if err != nil {
return dnsQueryTime, -1, err
2016-02-13 18:00:42 +00:00
}
m.SetQuestion(dns.Fqdn(domain), recordType)
m.RecursionDesired = true
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, -1, err
2016-02-13 18:00:42 +00:00
}
if r.Rcode != dns.RcodeSuccess {
return dnsQueryTime, r.Rcode, fmt.Errorf("Invalid answer (%s) from %s after %s query for %s", dns.RcodeToString[r.Rcode], server, d.RecordType, domain)
2016-02-13 18:00:42 +00:00
}
dnsQueryTime = float64(rtt.Nanoseconds()) / 1e6
return dnsQueryTime, r.Rcode, nil
2016-02-13 18:00:42 +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
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
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 setResult(result ResultType, fields map[string]interface{}, tags map[string]string) {
var tag string
switch result {
case Success:
tag = "success"
case Timeout:
tag = "timeout"
case Error:
tag = "error"
}
tags["result"] = tag
fields["result_code"] = uint64(result)
}
2016-02-13 18:00:42 +00:00
func init() {
inputs.Add("dns_query", func() telegraf.Input {
return &DnsQuery{}
2016-02-13 18:00:42 +00:00
})
}