Repository: prometheus/blackbox_exporter
Pull request #859 (2022-01-02 05:16:34)

Add control of recursion desired flag for DNS probes
This defaults recursion desired to on to preserve existing behaviour,
but allows it to be configured.

Asked for in a comment in #474 and generally useful.

Signed-off-by: David Leadbeater <dgl@dgl.cx>


Patch:
diff --git a/CONFIGURATION.md b/CONFIGURATION.md
index 5a8608e..9949ffe 100644
--- a/CONFIGURATION.md
+++ b/CONFIGURATION.md
@@ -189,6 +189,9 @@ query_name: <string>
 [ query_type: <string> | default = "ANY" ]
 [ query_class: <string> | default = "IN" ]
 
+# Set the recursion desired (RD) flag in the request.
+[ recursion: <boolean> | default = true ]
+
 # List of valid response codes.
 valid_rcodes:
   [ - <string> ... | default = "NOERROR" ]
diff --git a/config/config.go b/config/config.go
index e36b47c..b71d97e 100644
--- a/config/config.go
+++ b/config/config.go
@@ -82,6 +82,7 @@ var (
 	// DefaultDNSProbe set default value for DNSProbe
 	DefaultDNSProbe = DNSProbe{
 		IPProtocolFallback: true,
+		Recursion:          true,
 	}
 )
 
@@ -264,8 +265,9 @@ type DNSProbe struct {
 	TransportProtocol  string           `yaml:"transport_protocol,omitempty"`
 	QueryClass         string           `yaml:"query_class,omitempty"` // Defaults to IN.
 	QueryName          string           `yaml:"query_name,omitempty"`
-	QueryType          string           `yaml:"query_type,omitempty"`   // Defaults to ANY.
-	ValidRcodes        []string         `yaml:"valid_rcodes,omitempty"` // Defaults to NOERROR.
+	QueryType          string           `yaml:"query_type,omitempty"`        // Defaults to ANY.
+	Recursion          bool             `yaml:"recursion_desired,omitempty"` // Defaults to true.
+	ValidRcodes        []string         `yaml:"valid_rcodes,omitempty"`      // Defaults to NOERROR.
 	ValidateAnswer     DNSRRValidator   `yaml:"validate_answer_rrs,omitempty"`
 	ValidateAuthority  DNSRRValidator   `yaml:"validate_authority_rrs,omitempty"`
 	ValidateAdditional DNSRRValidator   `yaml:"validate_additional_rrs,omitempty"`
diff --git a/prober/dns.go b/prober/dns.go
index d3463e2..8e8a1b5 100644
--- a/prober/dns.go
+++ b/prober/dns.go
@@ -250,7 +250,7 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry
 
 	msg := new(dns.Msg)
 	msg.Id = dns.Id()
-	msg.RecursionDesired = true
+	msg.RecursionDesired = module.DNS.Recursion
 	msg.Question = make([]dns.Question, 1)
 	msg.Question[0] = dns.Question{dns.Fqdn(module.DNS.QueryName), qt, qc}
 
diff --git a/prober/http.go b/prober/http.go
index 7d5adc3..156feca 100644
--- a/prober/http.go
+++ b/prober/http.go
@@ -38,7 +38,6 @@ import (
 	"github.com/go-kit/log/level"
 	"github.com/prometheus/client_golang/prometheus"
 	pconfig "github.com/prometheus/common/config"
-	"github.com/prometheus/common/version"
 	"golang.org/x/net/publicsuffix"
 
 	"github.com/prometheus/blackbox_exporter/config"
@@ -233,8 +232,6 @@ func (bc *byteCounter) Read(p []byte) (int, error) {
 	return n, err
 }
 
-var userAgentDefaultHeader = fmt.Sprintf("Blackbox Exporter/%s", version.Version)
-
 func ProbeHTTP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger log.Logger) (success bool) {
 	var redirects int
 	var (
@@ -433,11 +430,6 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr
 		request.Header.Set(key, value)
 	}
 
-	_, hasUserAgent := request.Header["User-Agent"]
-	if !hasUserAgent {
-		request.Header.Set("User-Agent", userAgentDefaultHeader)
-	}
-
 	trace := &httptrace.ClientTrace{
 		DNSStart:             tt.DNSStart,
 		DNSDone:              tt.DNSDone,


Test patch:
diff --git a/prober/dns_test.go b/prober/dns_test.go
index 1d313d6..73385ae 100644
--- a/prober/dns_test.go
+++ b/prober/dns_test.go
@@ -69,16 +69,20 @@ func startDNSServer(protocol string, handler func(dns.ResponseWriter, *dns.Msg))
 func recursiveDNSHandler(w dns.ResponseWriter, r *dns.Msg) {
 	m := new(dns.Msg)
 	m.SetReply(r)
-	answers := []string{
-		"example.com. 3600 IN A 127.0.0.1",
-		"example.com. 3600 IN A 127.0.0.2",
-	}
-	for _, rr := range answers {
-		a, err := dns.NewRR(rr)
-		if err != nil {
-			panic(err)
+	if !r.RecursionDesired {
+		m.Rcode = dns.RcodeRefused
+	} else {
+		answers := []string{
+			"example.com. 3600 IN A 127.0.0.1",
+			"example.com. 3600 IN A 127.0.0.2",
+		}
+		for _, rr := range answers {
+			a, err := dns.NewRR(rr)
+			if err != nil {
+				panic(err)
+			}
+			m.Answer = append(m.Answer, a)
 		}
-		m.Answer = append(m.Answer, a)
 	}
 	if err := w.WriteMsg(m); err != nil {
 		panic(err)
@@ -99,6 +103,7 @@ func TestRecursiveDNSResponse(t *testing.T) {
 				IPProtocol:         "ip4",
 				IPProtocolFallback: true,
 				QueryName:          "example.com",
+				Recursion:          true,
 			}, true,
 		},
 		{
@@ -106,6 +111,7 @@ func TestRecursiveDNSResponse(t *testing.T) {
 				IPProtocol:         "ip4",
 				IPProtocolFallback: true,
 				QueryName:          "example.com",
+				Recursion:          true,
 				ValidRcodes:        []string{"SERVFAIL", "NXDOMAIN"},
 			}, false,
 		},
@@ -114,6 +120,7 @@ func TestRecursiveDNSResponse(t *testing.T) {
 				IPProtocol:         "ip4",
 				IPProtocolFallback: true,
 				QueryName:          "example.com",
+				Recursion:          true,
 				ValidateAnswer: config.DNSRRValidator{
 					FailIfMatchesRegexp:    []string{".*7200.*"},
 					FailIfNotMatchesRegexp: []string{".*3600.*"},
@@ -125,6 +132,7 @@ func TestRecursiveDNSResponse(t *testing.T) {
 				IPProtocol:         "ip4",
 				IPProtocolFallback: true,
 				QueryName:          "example.com",
+				Recursion:          true,
 				ValidateAuthority: config.DNSRRValidator{
 					FailIfMatchesRegexp: []string{".*7200.*"},
 				},
@@ -135,11 +143,20 @@ func TestRecursiveDNSResponse(t *testing.T) {
 				IPProtocol:         "ip4",
 				IPProtocolFallback: true,
 				QueryName:          "example.com",
+				Recursion:          true,
 				ValidateAdditional: config.DNSRRValidator{
 					FailIfNotMatchesRegexp: []string{".*3600.*"},
 				},
 			}, false,
 		},
+		{
+			config.DNSProbe{
+				IPProtocol:         "ip4",
+				IPProtocolFallback: true,
+				QueryName:          "example.com",
+				Recursion:          false,
+			}, false,
+		},
 	}
 
 	for _, protocol := range PROTOCOLS {
@@ -166,6 +183,9 @@ func TestRecursiveDNSResponse(t *testing.T) {
 				"probe_dns_authority_rrs":  0,
 				"probe_dns_additional_rrs": 0,
 			}
+			if !test.Probe.Recursion {
+				expectedResults["probe_dns_answer_rrs"] = 0
+			}
 			checkRegistryResults(expectedResults, mfs, t)
 		}
 	}
@@ -474,6 +494,7 @@ func TestDNSProtocol(t *testing.T) {
 				QueryName:         "example.com",
 				TransportProtocol: protocol,
 				IPProtocol:        "ip6",
+				Recursion:         true,
 			},
 		}
 		registry := prometheus.NewRegistry()
@@ -497,6 +518,7 @@ func TestDNSProtocol(t *testing.T) {
 			Timeout: time.Second,
 			DNS: config.DNSProbe{
 				QueryName:         "example.com",
+				Recursion:         true,
 				TransportProtocol: protocol,
 				IPProtocol:        "ip4",
 			},
@@ -523,6 +545,7 @@ func TestDNSProtocol(t *testing.T) {
 			Timeout: time.Second,
 			DNS: config.DNSProbe{
 				QueryName:         "example.com",
+				Recursion:         true,
 				TransportProtocol: protocol,
 			},
 		}
@@ -548,6 +571,7 @@ func TestDNSProtocol(t *testing.T) {
 			Timeout: time.Second,
 			DNS: config.DNSProbe{
 				QueryName: "example.com",
+				Recursion: true,
 			},
 		}
 		registry = prometheus.NewRegistry()
@@ -590,6 +614,7 @@ func TestDNSMetrics(t *testing.T) {
 			IPProtocol:         "ip4",
 			IPProtocolFallback: true,
 			QueryName:          "example.com",
+			Recursion:          true,
 		},
 	}
 	registry := prometheus.NewRegistry()