Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
254
plugins/inputs/dns_query/dns_query.go
Normal file
254
plugins/inputs/dns_query/dns_query.go
Normal file
|
@ -0,0 +1,254 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package dns_query
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"slices"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
var ignoredErrors = []string{
|
||||
"NXDOMAIN",
|
||||
}
|
||||
|
||||
type resultType uint64
|
||||
|
||||
const (
|
||||
successResult resultType = iota
|
||||
timeoutResult
|
||||
errorResult
|
||||
)
|
||||
|
||||
type DNSQuery struct {
|
||||
Domains []string `toml:"domains"`
|
||||
Network string `toml:"network"`
|
||||
Servers []string `toml:"servers"`
|
||||
RecordType string `toml:"record_type"`
|
||||
Port int `toml:"port"`
|
||||
Timeout config.Duration `toml:"timeout"`
|
||||
IncludeFields []string `toml:"include_fields"`
|
||||
|
||||
fieldEnabled map[string]bool
|
||||
}
|
||||
|
||||
func (*DNSQuery) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (d *DNSQuery) Init() error {
|
||||
// Convert the included fields into a lookup-table
|
||||
d.fieldEnabled = make(map[string]bool, len(d.IncludeFields))
|
||||
for _, f := range d.IncludeFields {
|
||||
switch f {
|
||||
case "first_ip", "all_ips":
|
||||
default:
|
||||
return fmt.Errorf("invalid field %q included", f)
|
||||
}
|
||||
d.fieldEnabled[f] = true
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
if d.Network == "" {
|
||||
d.Network = "udp"
|
||||
}
|
||||
|
||||
if d.RecordType == "" {
|
||||
d.RecordType = "NS"
|
||||
}
|
||||
|
||||
if len(d.Domains) == 0 {
|
||||
d.Domains = []string{"."}
|
||||
d.RecordType = "NS"
|
||||
}
|
||||
|
||||
if d.Port < 1 {
|
||||
d.Port = 53
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSQuery) Gather(acc telegraf.Accumulator) error {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, domain := range d.Domains {
|
||||
for _, server := range d.Servers {
|
||||
wg.Add(1)
|
||||
go func(domain, server string) {
|
||||
defer wg.Done()
|
||||
|
||||
fields, tags, err := d.query(domain, server)
|
||||
if err != nil && !slices.Contains(ignoredErrors, tags["rcode"]) {
|
||||
var opErr *net.OpError
|
||||
if !errors.As(err, &opErr) || !opErr.Timeout() {
|
||||
acc.AddError(err)
|
||||
}
|
||||
}
|
||||
acc.AddFields("dns_query", fields, tags)
|
||||
}(domain, server)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSQuery) query(domain, server string) (map[string]interface{}, map[string]string, error) {
|
||||
tags := map[string]string{
|
||||
"server": server,
|
||||
"domain": domain,
|
||||
"record_type": d.RecordType,
|
||||
"result": "error",
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"query_time_ms": float64(0),
|
||||
"result_code": uint64(errorResult),
|
||||
}
|
||||
|
||||
c := dns.Client{
|
||||
ReadTimeout: time.Duration(d.Timeout),
|
||||
Net: d.Network,
|
||||
}
|
||||
|
||||
recordType, err := d.parseRecordType()
|
||||
if err != nil {
|
||||
return fields, tags, err
|
||||
}
|
||||
|
||||
var msg dns.Msg
|
||||
msg.SetQuestion(dns.Fqdn(domain), recordType)
|
||||
msg.RecursionDesired = true
|
||||
|
||||
addr := net.JoinHostPort(server, strconv.Itoa(d.Port))
|
||||
r, rtt, err := c.Exchange(&msg, addr)
|
||||
if err != nil {
|
||||
var opErr *net.OpError
|
||||
if errors.As(err, &opErr) && opErr.Timeout() {
|
||||
tags["result"] = "timeout"
|
||||
fields["result_code"] = uint64(timeoutResult)
|
||||
return fields, tags, err
|
||||
}
|
||||
return fields, tags, err
|
||||
}
|
||||
|
||||
// Fill valid fields
|
||||
tags["rcode"] = dns.RcodeToString[r.Rcode]
|
||||
fields["rcode_value"] = r.Rcode
|
||||
fields["query_time_ms"] = float64(rtt.Nanoseconds()) / 1e6
|
||||
|
||||
// Handle the failure case
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return fields, tags, fmt.Errorf("invalid answer (%s) from %s after %s query for %s", dns.RcodeToString[r.Rcode], server, d.RecordType, domain)
|
||||
}
|
||||
|
||||
// Success
|
||||
tags["result"] = "success"
|
||||
fields["result_code"] = uint64(successResult)
|
||||
|
||||
// Fill out custom fields for specific record types
|
||||
for _, record := range r.Answer {
|
||||
switch x := record.(type) {
|
||||
case *dns.A:
|
||||
fields["name"] = x.Hdr.Name
|
||||
case *dns.AAAA:
|
||||
fields["name"] = x.Hdr.Name
|
||||
case *dns.CNAME:
|
||||
fields["name"] = x.Hdr.Name
|
||||
case *dns.MX:
|
||||
fields["name"] = x.Hdr.Name
|
||||
fields["preference"] = x.Preference
|
||||
case *dns.SOA:
|
||||
fields["expire"] = x.Expire
|
||||
fields["minttl"] = x.Minttl
|
||||
fields["name"] = x.Hdr.Name
|
||||
fields["refresh"] = x.Refresh
|
||||
fields["retry"] = x.Retry
|
||||
fields["serial"] = x.Serial
|
||||
}
|
||||
}
|
||||
|
||||
if d.fieldEnabled["first_ip"] {
|
||||
for _, record := range r.Answer {
|
||||
if ip, found := extractIP(record); found {
|
||||
fields["ip"] = ip
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if d.fieldEnabled["all_ips"] {
|
||||
for i, record := range r.Answer {
|
||||
if ip, found := extractIP(record); found {
|
||||
fields["ip_"+strconv.Itoa(i)] = ip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fields, tags, nil
|
||||
}
|
||||
|
||||
func (d *DNSQuery) parseRecordType() (uint16, error) {
|
||||
var recordType uint16
|
||||
var err error
|
||||
|
||||
switch d.RecordType {
|
||||
case "A":
|
||||
recordType = dns.TypeA
|
||||
case "AAAA":
|
||||
recordType = dns.TypeAAAA
|
||||
case "ANY":
|
||||
recordType = dns.TypeANY
|
||||
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
|
||||
case "TXT":
|
||||
recordType = dns.TypeTXT
|
||||
default:
|
||||
err = fmt.Errorf("record type %s not recognized", d.RecordType)
|
||||
}
|
||||
|
||||
return recordType, err
|
||||
}
|
||||
|
||||
func extractIP(record dns.RR) (string, bool) {
|
||||
if r, ok := record.(*dns.A); ok {
|
||||
return r.A.String(), true
|
||||
}
|
||||
if r, ok := record.(*dns.AAAA); ok {
|
||||
return r.AAAA.String(), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("dns_query", func() telegraf.Input {
|
||||
return &DNSQuery{
|
||||
Timeout: config.Duration(2 * time.Second),
|
||||
}
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue