Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ed6d767
Basic Infrastructure for Handling probe failures
slrtbtfs Dec 6, 2024
d3be6eb
Report Probe Results for grpc Prober
slrtbtfs Dec 6, 2024
b003e9c
Report Probe Results for HTTP Prober
slrtbtfs Dec 6, 2024
df8c331
Improve Help Text
slrtbtfs Dec 6, 2024
39bcb42
Report Probe Results for TCP Prober
slrtbtfs Dec 6, 2024
747e8b5
Report Probe Results for ICMP Prober
slrtbtfs Dec 6, 2024
5c48039
Report Probe Results for DNS Prober
slrtbtfs Dec 6, 2024
4398c7f
Fix label generation
slrtbtfs Dec 10, 2024
9825c25
Fix tests
slrtbtfs Dec 10, 2024
1148d21
Remove raw errors from Prober result
slrtbtfs Dec 12, 2024
8704c54
Log remaining errors
slrtbtfs Dec 12, 2024
655ed2b
Fix SSL Log Test
slrtbtfs Dec 12, 2024
4a60a60
Put failure results on one log line
slrtbtfs Dec 12, 2024
13f0367
Fix typo
slrtbtfs Dec 12, 2024
a76e580
fix tests after rebase
slrtbtfs Feb 25, 2025
0fc7c76
Check error messages in DNS tests
slrtbtfs Feb 25, 2025
0d9639f
Check Failure Reasons in grpc test
slrtbtfs Feb 25, 2025
3547ee0
WIP: Check Failure reasons in HTTP test
slrtbtfs Feb 25, 2025
2ea73c5
Use new result type in http tests
slrtbtfs Feb 27, 2025
8df2769
Switch more tests over to ProbeResult
slrtbtfs Feb 27, 2025
01c0162
Use new Data Types in all tests
slrtbtfs Feb 27, 2025
9b081ba
Use correct string formatting
slrtbtfs Feb 27, 2025
ab92c7d
Documentation for ProbeResult type
slrtbtfs Feb 27, 2025
2753cca
Correct string formatting
slrtbtfs Feb 28, 2025
26cca1a
rework grpc tests
slrtbtfs Feb 28, 2025
4553a8f
WIP: Fix tests after rebase
slrtbtfs Apr 11, 2025
accbc90
Fix tests after rebase
slrtbtfs Apr 11, 2025
692d921
Grammar
slrtbtfs Apr 11, 2025
333e0b9
Instrument TLS and TCP Code
slrtbtfs Apr 28, 2025
a5eefb9
Add CI-Skip
slrtbtfs Apr 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 48 additions & 63 deletions prober/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"log/slog"
"net"
"regexp"
"strconv"
"time"

"github.com/miekg/dns"
Expand All @@ -28,37 +29,36 @@ import (
)

// validRRs checks a slice of RRs received from the server against a DNSRRValidator.
func validRRs(rrs *[]dns.RR, v *config.DNSRRValidator, logger *slog.Logger) bool {
func validRRs(rrType string, rrs *[]dns.RR, v *config.DNSRRValidator, logger *slog.Logger) ProbeResult {
logger.Info("Validating " + rrType)
failure_reason := rrType + " validation Failed"
var anyMatch = false
var allMatch = true
// Fail the probe if there are no RRs of a given type, but a regexp match is required
// (i.e. FailIfNotMatchesRegexp or FailIfNoneMatchesRegexp is set).
if len(*rrs) == 0 && len(v.FailIfNotMatchesRegexp) > 0 {
logger.Error("fail_if_not_matches_regexp specified but no RRs returned")
return false
return ProbeFailure(failure_reason, "problem", "fail_if_not_matches_regexp specified but no RRs returned")
}
if len(*rrs) == 0 && len(v.FailIfNoneMatchesRegexp) > 0 {
logger.Error("fail_if_none_matches_regexp specified but no RRs returned")
return false
return ProbeFailure(failure_reason, "problem", "fail_if_none_matches_regexp specified but no RRs returned")
}
for _, rr := range *rrs {
logger.Info("Validating RR", "rr", rr)
for _, re := range v.FailIfMatchesRegexp {
match, err := regexp.MatchString(re, rr.String())
if err != nil {
logger.Error("Error matching regexp", "regexp", re, "err", err)
return false
logger.Error(err.Error())
return ProbeFailure(failure_reason, "problem", "Error matching regexp", "regexp", re)
}
if match {
logger.Error("At least one RR matched regexp", "regexp", re, "rr", rr)
return false
return ProbeFailure(failure_reason, "problem", "At least one RR matched regexp", "regexp", re)
}
}
for _, re := range v.FailIfAllMatchRegexp {
match, err := regexp.MatchString(re, rr.String())
if err != nil {
logger.Error("Error matching regexp", "regexp", re, "err", err)
return false
logger.Error(err.Error())
return ProbeFailure(failure_reason, "problem", "Error matching regexp", "regexp", re)
}
if !match {
allMatch = false
Expand All @@ -67,38 +67,35 @@ func validRRs(rrs *[]dns.RR, v *config.DNSRRValidator, logger *slog.Logger) bool
for _, re := range v.FailIfNotMatchesRegexp {
match, err := regexp.MatchString(re, rr.String())
if err != nil {
logger.Error("Error matching regexp", "regexp", re, "err", err)
return false
logger.Error(err.Error())
return ProbeFailure(failure_reason, "problem", "Error matching regexp", "regexp", re)
}
if !match {
logger.Error("At least one RR did not match regexp", "regexp", re, "rr", rr)
return false
return ProbeFailure(failure_reason, "problem", "At least one RR did not match regexp", "regexp", re)
}
}
for _, re := range v.FailIfNoneMatchesRegexp {
match, err := regexp.MatchString(re, rr.String())
if err != nil {
logger.Error("Error matching regexp", "regexp", re, "err", err)
return false
logger.Error(err.Error())
return ProbeFailure(failure_reason, "problem", "Error matching regexp", "regexp", re)
}
if match {
anyMatch = true
}
}
}
if len(v.FailIfAllMatchRegexp) > 0 && !allMatch {
logger.Error("Not all RRs matched regexp")
return false
return ProbeFailure(failure_reason, "problem", "Not all RRs matched regexp")
}
if len(v.FailIfNoneMatchesRegexp) > 0 && !anyMatch {
logger.Error("None of the RRs did matched any regexp")
return false
return ProbeFailure(failure_reason, "problem", "None of the RRs matched any regexp")
}
return true
return ProbeSuccess()
}

// validRcode checks rcode in the response against a list of valid rcodes.
func validRcode(rcode int, valid []string, logger *slog.Logger) bool {
func validRcode(rcode int, valid []string, logger *slog.Logger) ProbeResult {
var validRcodes []int
// If no list of valid rcodes is specified, only NOERROR is considered valid.
if valid == nil {
Expand All @@ -107,23 +104,23 @@ func validRcode(rcode int, valid []string, logger *slog.Logger) bool {
for _, rcode := range valid {
rc, ok := dns.StringToRcode[rcode]
if !ok {
logger.Error("Invalid rcode", "rcode", rcode, "known_rcode", dns.RcodeToString)
return false
logger.Info("Known rcodes", "known_rcode", dns.RcodeToString)
return ProbeFailure("Invalid rcode", "rcode", rcode)
}
validRcodes = append(validRcodes, rc)
}
}
for _, rc := range validRcodes {
if rcode == rc {
logger.Info("Rcode is valid", "rcode", rcode, "string_rcode", dns.RcodeToString[rcode])
return true
return ProbeSuccess()
}
}
logger.Error("Rcode is not one of the valid rcodes", "rcode", rcode, "string_rcode", dns.RcodeToString[rcode], "valid_rcodes", validRcodes)
return false
logger.Info("Valid Rrcodes", "valid_rcodes", validRcodes)
return ProbeFailure("Rcode is not one of the valid rcodes", "rcode", strconv.Itoa(rcode), "string_rcode", dns.RcodeToString[rcode])
}

func ProbeDNS(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger *slog.Logger) bool {
func ProbeDNS(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger *slog.Logger) ProbeResult {
var dialProtocol string
probeDNSDurationGaugeVec := prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "probe_dns_duration_seconds",
Expand Down Expand Up @@ -161,8 +158,8 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry
var ok bool
qc, ok = dns.StringToClass[module.DNS.QueryClass]
if !ok {
logger.Error("Invalid query class", "Class seen", module.DNS.QueryClass, "Existing classes", dns.ClassToString)
return false
logger.Info("Existing query classes", "existing_classes", dns.ClassToString)
return ProbeFailure("Invalid query class", "Class seen", module.DNS.QueryClass)
}
}

Expand All @@ -171,8 +168,8 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry
var ok bool
qt, ok = dns.StringToType[module.DNS.QueryType]
if !ok {
logger.Error("Invalid query type", "Type seen", module.DNS.QueryType, "Existing types", dns.TypeToString)
return false
logger.Info("Existing query types", "existing_types", dns.TypeToString)
return ProbeFailure("Invalid query type", "Type seen", module.DNS.QueryType)
}
}
var probeDNSSOAGauge prometheus.Gauge
Expand All @@ -182,8 +179,7 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry
module.DNS.TransportProtocol = "udp"
}
if module.DNS.TransportProtocol != "udp" && module.DNS.TransportProtocol != "tcp" {
logger.Error("Configuration error: Expected transport protocol udp or tcp", "protocol", module.DNS.TransportProtocol)
return false
return ProbeFailure("Configuration error: Expected transport protocol udp or tcp", "protocol", module.DNS.TransportProtocol)
}

targetAddr, port, err := net.SplitHostPort(target)
Expand All @@ -196,10 +192,9 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry
}
targetAddr = target
}
ip, lookupTime, err := chooseProtocol(ctx, module.DNS.IPProtocol, module.DNS.IPProtocolFallback, targetAddr, registry, logger)
if err != nil {
logger.Error("Error resolving address", "err", err)
return false
ip, lookupTime, resolveResult := chooseProtocol(ctx, module.DNS.IPProtocol, module.DNS.IPProtocolFallback, targetAddr, registry, logger)
if !resolveResult.success {
return resolveResult
}
probeDNSDurationGaugeVec.WithLabelValues("resolve").Add(lookupTime)
targetIP := net.JoinHostPort(ip.String(), port)
Expand All @@ -214,8 +209,7 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry
if module.DNS.TransportProtocol == "tcp" {
dialProtocol += "-tls"
} else {
logger.Error("Configuration error: Expected transport protocol tcp for DoT", "protocol", module.DNS.TransportProtocol)
return false
return ProbeFailure("Configuration error: Expected transport protocol tcp for DoT", "protocol", module.DNS.TransportProtocol)
}
}

Expand All @@ -225,8 +219,8 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry
if module.DNS.DNSOverTLS {
tlsConfig, err := pconfig.NewTLSConfig(&module.DNS.TLSConfig)
if err != nil {
logger.Error("Failed to create TLS configuration", "err", err)
return false
logger.Error(err.Error())
return ProbeFailure("Failed to create TLS configuration")
}
if tlsConfig.ServerName == "" {
// Use target-hostname as default for TLS-servername.
Expand All @@ -240,8 +234,7 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry
if len(module.DNS.SourceIPAddress) > 0 {
srcIP := net.ParseIP(module.DNS.SourceIPAddress)
if srcIP == nil {
logger.Error("Error parsing source ip address", "srcIP", module.DNS.SourceIPAddress)
return false
return ProbeFailure("Error parsing source ip address", "srcIP", module.DNS.SourceIPAddress)
}
logger.Info("Using local address", "srcIP", srcIP)
client.Dialer = &net.Dialer{}
Expand Down Expand Up @@ -270,8 +263,8 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry
probeDNSDurationGaugeVec.WithLabelValues("connect").Set((time.Since(requestStart) - rtt).Seconds())
probeDNSDurationGaugeVec.WithLabelValues("request").Set(rtt.Seconds())
if err != nil {
logger.Error("Error while sending a DNS query", "err", err)
return false
logger.Error(err.Error())
return ProbeFailure("Error while sending a DNS query")
}
logger.Info("Got response", "response", response)

Expand All @@ -294,23 +287,15 @@ func ProbeDNS(ctx context.Context, target string, module config.Module, registry
}
}

if !validRcode(response.Rcode, module.DNS.ValidRcodes, logger) {
return false
}
logger.Info("Validating Answer RRs")
if !validRRs(&response.Answer, &module.DNS.ValidateAnswer, logger) {
logger.Error("Answer RRs validation failed")
return false
result := validRcode(response.Rcode, module.DNS.ValidRcodes, logger)
if result.success {
result = validRRs("Answer RRs", &response.Answer, &module.DNS.ValidateAnswer, logger)
}
logger.Info("Validating Authority RRs")
if !validRRs(&response.Ns, &module.DNS.ValidateAuthority, logger) {
logger.Error("Authority RRs validation failed")
return false
if result.success {
result = validRRs("Authority RRs", &response.Ns, &module.DNS.ValidateAuthority, logger)
}
logger.Info("Validating Additional RRs")
if !validRRs(&response.Extra, &module.DNS.ValidateAdditional, logger) {
logger.Error("Additional RRs validation failed")
return false
if result.success {
result = validRRs("Additional RRs", &response.Extra, &module.DNS.ValidateAdditional, logger)
}
return true
return result
}
Loading