1use crate::dns_parser;
2use crate::errors::{DnsError, EtchDnsResult};
3use crate::stats::SharedStats;
4use log::{debug, error, info, warn};
5use rand::RngExt;
6use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
7use std::sync::Arc;
8use std::time::{Duration, Instant};
9use tokio::net::UdpSocket;
10use tokio::time;
11
12fn wildcard_address(peer: SocketAddr) -> SocketAddr {
13 let ip = if peer.is_ipv4() {
14 IpAddr::V4(Ipv4Addr::UNSPECIFIED)
15 } else {
16 IpAddr::V6(Ipv6Addr::UNSPECIFIED)
17 };
18 SocketAddr::new(ip, 0)
19}
20
21#[allow(dead_code)]
23const MIN_PROBE_INTERVAL_SECS: u64 = 60;
24
25#[allow(dead_code)]
27pub struct ServerProber {
28 upstream_servers: Vec<String>,
30
31 stats: Arc<SharedStats>,
33
34 probe_interval: u64,
36
37 probe_timeout: u64,
39}
40
41#[allow(dead_code)]
42impl ServerProber {
43 #[allow(dead_code)]
48 pub fn new(
49 upstream_servers: Vec<String>,
50 stats: Arc<SharedStats>,
51 server_timeout: u64,
52 probe_interval: Option<u64>,
53 probe_timeout: Option<u64>,
54 ) -> Self {
55 let default_probe_timeout = server_timeout;
57
58 let default_probe_interval = std::cmp::max(MIN_PROBE_INTERVAL_SECS, server_timeout);
60
61 Self {
62 upstream_servers,
63 stats,
64 probe_interval: probe_interval.unwrap_or(default_probe_interval),
65 probe_timeout: probe_timeout.unwrap_or(default_probe_timeout),
66 }
67 }
68
69 pub fn start(self) {
71 tokio::spawn(async move {
72 info!(
73 "Starting server prober with interval of {} seconds",
74 self.probe_interval
75 );
76
77 let mut interval = time::interval(Duration::from_secs(self.probe_interval));
79
80 loop {
81 interval.tick().await;
83
84 self.probe_all_servers().await;
86 }
87 });
88 }
89
90 async fn probe_all_servers(&self) {
92 debug!("Probing all upstream DNS servers");
93
94 for server in &self.upstream_servers {
95 match server.parse::<SocketAddr>() {
97 Ok(addr) => {
98 match self.probe_server(addr).await {
100 Ok(response_time) => {
101 debug!("Probe to {addr} completed in {response_time:.2?}");
102
103 self.stats.record_success(addr, response_time).await;
105 }
106 Err(e) => {
107 warn!("Probe to {addr} failed: {e}");
108
109 self.stats.record_failure(addr).await;
111 }
112 }
113 }
114 Err(e) => {
115 error!("Failed to parse server address {server}: {e}");
116 }
117 }
118 }
119 }
120
121 async fn probe_server(&self, server_addr: SocketAddr) -> EtchDnsResult<Duration> {
123 let query = self.create_random_query();
125
126 let socket = UdpSocket::bind(wildcard_address(server_addr))
128 .await
129 .map_err(|e| {
130 DnsError::UpstreamError(format!("Failed to bind socket for probe: {e}"))
131 })?;
132
133 let start_time = Instant::now();
135
136 socket.send_to(&query, server_addr).await.map_err(|e| {
138 DnsError::UpstreamError(format!("Failed to send probe to {server_addr}: {e}"))
139 })?;
140
141 let mut buf = vec![0u8; dns_parser::DNS_MAX_PACKET_SIZE];
143
144 match time::timeout(
146 Duration::from_secs(self.probe_timeout),
147 socket.recv_from(&mut buf),
148 )
149 .await
150 {
151 Ok(Ok((len, response_addr))) => {
152 if response_addr != server_addr {
153 return Err(DnsError::UpstreamError(format!(
154 "Unexpected probe response source {response_addr}, expected {server_addr}"
155 ))
156 .into());
157 }
158
159 if len < 2 || buf[0] != query[0] || buf[1] != query[1] {
160 return Err(DnsError::UpstreamError(format!(
161 "Mismatched DNS transaction ID in response from {server_addr}"
162 ))
163 .into());
164 }
165
166 let response_time = start_time.elapsed();
168
169 if let Err(e) = dns_parser::validate_dns_response(&buf[..len]) {
171 return Err(DnsError::UpstreamError(format!(
172 "Invalid DNS response from {server_addr}: {e}"
173 ))
174 .into());
175 }
176
177 Ok(response_time)
178 }
179 Ok(Err(e)) => {
180 Err(DnsError::UpstreamError(format!(
182 "Failed to receive response from {server_addr}: {e}"
183 ))
184 .into())
185 }
186 Err(_) => {
187 Err(DnsError::UpstreamTimeout.into())
189 }
190 }
191 }
192
193 fn create_random_query(&self) -> Vec<u8> {
195 let mut query = vec![
198 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, b'g', b'o', b'o', b'g', b'l', b'e', 0x03, b'c', b'o', b'm',
206 0x00, 0x00, 0x01, 0x00, 0x01, ];
210
211 let tid: u16 = rand::rng().random();
213 query[0] = (tid >> 8) as u8;
214 query[1] = tid as u8;
215
216 query
217 }
218}
219
220pub async fn probe_server(server_addr: SocketAddr, timeout_secs: u64) -> EtchDnsResult<Duration> {
223 let query = create_random_query();
225
226 let socket = UdpSocket::bind(wildcard_address(server_addr))
228 .await
229 .map_err(|e| DnsError::UpstreamError(format!("Failed to bind socket for probe: {e}")))?;
230
231 let start_time = Instant::now();
233
234 socket.send_to(&query, server_addr).await.map_err(|e| {
236 DnsError::UpstreamError(format!("Failed to send probe to {server_addr}: {e}"))
237 })?;
238
239 let mut buf = vec![0u8; dns_parser::DNS_MAX_PACKET_SIZE];
241
242 match time::timeout(
244 Duration::from_secs(timeout_secs),
245 socket.recv_from(&mut buf),
246 )
247 .await
248 {
249 Ok(Ok((len, response_addr))) => {
250 if response_addr != server_addr {
251 return Err(DnsError::UpstreamError(format!(
252 "Unexpected probe response source {response_addr}, expected {server_addr}"
253 ))
254 .into());
255 }
256
257 if len < 2 || buf[0] != query[0] || buf[1] != query[1] {
258 return Err(DnsError::UpstreamError(format!(
259 "Mismatched DNS transaction ID in response from {server_addr}"
260 ))
261 .into());
262 }
263
264 let response_time = start_time.elapsed();
266
267 if let Err(e) = dns_parser::validate_dns_response(&buf[..len]) {
269 return Err(DnsError::UpstreamError(format!(
270 "Invalid DNS response from {server_addr}: {e}"
271 ))
272 .into());
273 }
274
275 Ok(response_time)
276 }
277 Ok(Err(e)) => {
278 Err(DnsError::UpstreamError(format!(
280 "Failed to receive response from {server_addr}: {e}"
281 ))
282 .into())
283 }
284 Err(_) => {
285 Err(DnsError::UpstreamTimeout.into())
287 }
288 }
289}
290
291fn create_random_query() -> Vec<u8> {
294 let mut query = vec![
297 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, b'g', b'o', b'o', b'g', b'l', b'e', 0x03, b'c', b'o', b'm',
305 0x00, 0x00, 0x01, 0x00, 0x01, ];
309
310 let tid: u16 = rand::rng().random_range(0..65535);
312 query[0] = (tid >> 8) as u8;
313 query[1] = tid as u8;
314
315 query
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321 use std::net::SocketAddr;
322 use std::time::Duration;
323
324 #[test]
325 fn test_wildcard_address_matches_peer_family() {
326 let ipv4 = wildcard_address("192.0.2.1:53".parse().unwrap());
327 let ipv6 = wildcard_address("[2001:db8::1]:53".parse().unwrap());
328
329 assert!(ipv4.is_ipv4());
330 assert!(ipv6.is_ipv6());
331 assert_eq!(ipv4.port(), 0);
332 assert_eq!(ipv6.port(), 0);
333 }
334
335 #[tokio::test]
336 async fn test_create_random_query() {
337 let stats = Arc::new(SharedStats::new());
338 let server_timeout = 5; let prober = ServerProber::new(
340 vec!["8.8.8.8:53".to_string()],
341 stats,
342 server_timeout,
343 None,
344 None,
345 );
346
347 let query = prober.create_random_query();
348
349 assert!(query.len() > 12); assert_eq!(query[2], 0x01); assert_eq!(query[3], 0x00); assert_eq!(query[4], 0x00);
358 assert_eq!(query[5], 0x01);
359
360 assert_eq!(query[6], 0x00);
362 assert_eq!(query[7], 0x00);
363 assert_eq!(query[8], 0x00);
364 assert_eq!(query[9], 0x00);
365 assert_eq!(query[10], 0x00);
366 assert_eq!(query[11], 0x00);
367 }
368
369 #[tokio::test]
370 async fn test_probe_server() {
371 let stats = Arc::new(SharedStats::new());
374 let server_timeout = 5; let prober = ServerProber::new(
376 vec!["8.8.8.8:53".to_string()],
377 stats.clone(),
378 server_timeout,
379 None,
380 None,
381 );
382
383 let addr = "8.8.8.8:53".parse::<SocketAddr>().unwrap();
385 match prober.probe_server(addr).await {
386 Ok(response_time) => {
387 assert!(response_time > Duration::from_micros(0));
389
390 stats.record_success(addr, response_time).await;
392
393 let resolvers_by_speed = stats.get_resolvers_by_speed().await;
395 assert!(!resolvers_by_speed.is_empty());
396 }
397 Err(e) => {
398 println!("Probe failed: {}", e);
401 }
403 }
404 }
405}