1use crate::check::{Timing, TimingBuilder};
2use mtop_client::dns::{DefaultDnsClient, DnsClient, Name, RecordClass, RecordType, ResponseCode};
3use std::sync::atomic::{AtomicBool, Ordering};
4use std::sync::Arc;
5use std::time::{Duration, Instant};
6use tokio::time;
7use tracing::{Instrument, Level};
8
9#[derive(Debug)]
11pub struct DnsPinger {
12 client: DefaultDnsClient,
13 interval: Duration,
14 stop: Arc<AtomicBool>,
15}
16
17impl DnsPinger {
18 pub fn new(client: DefaultDnsClient, interval: Duration, stop: Arc<AtomicBool>) -> Self {
21 Self { client, interval, stop }
22 }
23
24 pub async fn run(&self, name: Name, rtype: RecordType, rclass: RecordClass, count: u64) -> Bundle {
27 let mut timing_builder = TimingBuilder::default();
28 let mut total = 0;
29 let mut protocol_errors = 0;
30 let mut fatal_errors = 0;
31
32 let mut interval = time::interval(self.interval);
33
34 while !self.stop.load(Ordering::Acquire) && (count == 0 || total < count) {
35 let _ = interval.tick().await;
36 let start = Instant::now();
40
41 match self
42 .client
43 .resolve(name.clone(), rtype, rclass)
44 .instrument(tracing::span!(Level::INFO, "client.resolve"))
45 .await
46 {
47 Ok(r) => {
48 let min_ttl = r.answers().iter().map(|a| a.ttl()).min().unwrap_or(0);
49 let elapsed = start.elapsed();
50 timing_builder.add(elapsed);
51
52 if r.flags().get_response_code() != ResponseCode::NoError {
55 tracing::warn!(
56 id = %r.id(),
57 name = %name,
58 response_code = ?r.flags().get_response_code(),
59 num_questions = r.questions().len(),
60 num_answers = r.answers().len(),
61 num_authority = r.authority().len(),
62 num_extra = r.extra().len(),
63 min_ttl = min_ttl,
64 elapsed = ?elapsed,
65 );
66 protocol_errors += 1;
67 } else {
68 tracing::info!(
69 id = %r.id(),
70 name = %name,
71 response_code = ?r.flags().get_response_code(),
72 num_questions = r.questions().len(),
73 num_answers = r.answers().len(),
74 num_authority = r.authority().len(),
75 num_extra = r.extra().len(),
76 min_ttl = min_ttl,
77 elapsed = ?elapsed,
78 );
79 }
80 }
81 Err(e) => {
82 tracing::error!(message = "failed to resolve", name = %name, err = %e);
83 fatal_errors += 1;
84 }
85 }
86
87 total += 1;
88 }
89
90 let timing = timing_builder.build();
91 Bundle {
92 timing,
93 total,
94 protocol_errors,
95 fatal_errors,
96 }
97 }
98}
99
100#[derive(Debug, Default, Clone, Eq, PartialEq)]
101pub struct Bundle {
102 pub timing: Timing,
103 pub total: u64,
104 pub protocol_errors: u64,
105 pub fatal_errors: u64,
106}