1#[cfg(not(test))]
9use std::time::{Duration, Instant};
10use std::{
11 cmp,
12 fmt::Debug,
13 marker::PhantomData,
14 net::IpAddr,
15 sync::{
16 Arc,
17 atomic::{AtomicU8, AtomicU32, Ordering},
18 },
19};
20
21use futures_util::lock::Mutex as AsyncMutex;
22use parking_lot::Mutex as SyncMutex;
23#[cfg(test)]
24use tokio::time::{Duration, Instant};
25use tracing::{debug, error, warn};
26
27#[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
28use crate::metrics::opportunistic_encryption::ProbeMetrics;
29#[cfg(feature = "metrics")]
30use crate::metrics::{NameServerAddr, ResolverMetrics};
31use crate::{
32 config::{
33 ConnectionConfig, NameServerConfig, OpportunisticEncryption, ResolverOpts,
34 ServerOrderingStrategy,
35 },
36 connection_provider::ConnectionProvider,
37 name_server_pool::{NameServerTransportState, PoolContext},
38 net::{
39 DnsError, NetError, NoRecords,
40 runtime::{RuntimeProvider, Spawn},
41 xfer::{DnsHandle, FirstAnswer, Protocol},
42 },
43 proto::{
44 op::{DnsRequest, DnsRequestOptions, DnsResponse, Query, ResponseCode},
45 rr::{Name, RecordType},
46 },
47};
48
49pub struct NameServer<P: ConnectionProvider> {
54 config: NameServerConfig,
55 connections: AsyncMutex<Vec<ConnectionState<P>>>,
56 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
58 opportunistic_probe_metrics: ProbeMetrics,
59 #[cfg(feature = "metrics")]
61 resolver_metrics: ResolverMetrics,
62 server_srtt: DecayingSrtt,
63 connection_provider: P,
64}
65
66impl<P: ConnectionProvider> NameServer<P> {
67 pub fn new(
71 connections: impl IntoIterator<Item = (Protocol, P::Conn)>,
72 config: NameServerConfig,
73 options: &ResolverOpts,
74 connection_provider: P,
75 ) -> Self {
76 let mut connections = connections
77 .into_iter()
78 .map(|(protocol, handle)| ConnectionState::new(handle, protocol))
79 .collect::<Vec<_>>();
80
81 if options.server_ordering_strategy != ServerOrderingStrategy::UserProvidedOrder {
84 connections.sort_by_key(|ns| ns.protocol != Protocol::Udp);
85 }
86
87 #[cfg(feature = "metrics")]
88 let resolver_metrics = ResolverMetrics::new(match options.enable_per_name_server_metrics {
89 true => NameServerAddr::Individual(config.ip),
90 false => NameServerAddr::Aggregated,
91 });
92
93 Self {
94 config,
95 connections: AsyncMutex::new(connections),
96 server_srtt: DecayingSrtt::new(Duration::from_micros(rand::random_range(1..32))),
97 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
98 opportunistic_probe_metrics: ProbeMetrics::default(),
99 #[cfg(feature = "metrics")]
100 resolver_metrics,
101 connection_provider,
102 }
103 }
104
105 pub(crate) async fn send(
107 self: Arc<Self>,
108 request: DnsRequest,
109 policy: ConnectionPolicy,
110 cx: &Arc<PoolContext>,
111 ) -> Result<DnsResponse, NetError> {
112 let (_protocol, result) = self.send_inner(request, policy, cx).await;
113 #[cfg(feature = "metrics")]
114 if let Some(protocol) = _protocol {
115 self.resolver_metrics
116 .increment_query_result(&protocol, result.as_ref().map(|_| ()));
117 }
118
119 result
120 }
121
122 async fn send_inner(
123 &self,
124 request: DnsRequest,
125 policy: ConnectionPolicy,
126 cx: &Arc<PoolContext>,
127 ) -> (Option<Protocol>, Result<DnsResponse, NetError>) {
128 let mut reconnect_budget = 1u8;
131 loop {
132 let ConnectedClient {
133 handle,
134 meta,
135 protocol,
136 reuse,
137 } = match self.connected_mut_client(policy, cx).await {
138 Ok(v) => v,
139 Err((err, protocol)) => {
140 debug!(config = ?self.config, ?protocol, %err, "failed to establish connection to name server");
141 return (protocol, Err(err));
142 }
143 };
144 #[cfg(feature = "metrics")]
145 self.resolver_metrics.increment_outgoing_query(&protocol);
146 let now = Instant::now();
147 let response = handle.send(request.clone()).first_answer().await;
148 let rtt = now.elapsed();
149
150 match response {
151 Ok(response) => {
152 meta.set_status(Status::Established);
153 let result = DnsError::from_response(response);
154 let error = match result {
155 Ok(response) => {
156 meta.srtt.record(rtt);
157 self.server_srtt.record(rtt);
158 if cx.opportunistic_encryption.is_enabled() && protocol.is_encrypted() {
159 cx.transport_state()
160 .await
161 .response_received(self.config.ip, protocol);
162 }
163 return (Some(protocol), Ok(response));
164 }
165 Err(error) => error,
166 };
167
168 let update = match error {
169 DnsError::NoRecordsFound(NoRecords {
170 response_code: ResponseCode::ServFail,
171 ..
172 }) => Some(true),
173 DnsError::NoRecordsFound(NoRecords { .. }) => Some(false),
174 _ => None,
175 };
176
177 match update {
178 Some(true) => {
179 meta.srtt.record(rtt);
180 self.server_srtt.record(rtt);
181 }
182 Some(false) => {
183 meta.srtt.record_failure();
185 self.server_srtt.record_failure();
186 }
187 None => {}
188 }
189
190 let err = NetError::from(error);
191 if cx.opportunistic_encryption.is_enabled() && protocol.is_encrypted() {
192 cx.transport_state()
193 .await
194 .error_received(self.config.ip, protocol, &err)
195 }
196 return (Some(protocol), Err(err));
197 }
198 Err(error) => {
199 debug!(config = ?self.config, %error, "failed to connect to name server");
200
201 meta.set_status(Status::Failed);
203
204 if reuse == ConnectionReuse::Reused
208 && reconnect_budget > 0
209 && error.is_connection_closed()
210 {
211 #[cfg(feature = "metrics")]
212 self.resolver_metrics
214 .increment_query_result(&protocol, Err(&error));
215 reconnect_budget -= 1;
216 continue;
217 }
218
219 match &error {
223 NetError::Busy | NetError::Io(_) | NetError::Timeout => {
224 meta.srtt.record_failure();
225 self.server_srtt.record_failure();
226 }
227 #[cfg(feature = "__quic")]
228 NetError::QuinnConfigError(_)
229 | NetError::QuinnConnect(_)
230 | NetError::QuinnConnection(_)
231 | NetError::QuinnTlsConfigError(_) => {
232 meta.srtt.record_failure();
233 self.server_srtt.record_failure();
234 }
235 #[cfg(feature = "__tls")]
236 NetError::RustlsError(_) => {
237 meta.srtt.record_failure();
238 self.server_srtt.record_failure();
239 }
240 _ => {}
241 }
242
243 if cx.opportunistic_encryption.is_enabled() && protocol.is_encrypted() {
244 cx.transport_state()
245 .await
246 .error_received(self.config.ip, protocol, &error);
247 }
248
249 return (Some(protocol), Err(error));
251 }
252 }
253 }
254 }
255
256 async fn connected_mut_client(
261 &self,
262 policy: ConnectionPolicy,
263 cx: &Arc<PoolContext>,
264 ) -> Result<ConnectedClient<P>, (NetError, Option<Protocol>)> {
265 {
267 let mut connections = self.connections.lock().await;
268 connections
269 .retain(|conn| matches!(conn.meta.status(), Status::Init | Status::Established));
270 if let Some(conn) = policy.select_connection(
271 self.config.ip,
272 &*cx.transport_state().await,
273 &cx.opportunistic_encryption,
274 &connections,
275 ) {
276 return Ok(ConnectedClient {
277 handle: conn.handle.clone(),
278 meta: conn.meta.clone(),
279 protocol: conn.protocol,
280 reuse: ConnectionReuse::Reused,
281 });
282 }
283 }
284
285 debug!(config = ?self.config, "connecting");
287 let config = policy
288 .select_connection_config(
289 self.config.ip,
290 &*cx.transport_state().await,
291 &cx.opportunistic_encryption,
292 &self.config.connections,
293 )
294 .ok_or((NetError::NoConnections, None))?;
295
296 let protocol = config.protocol.to_protocol();
297 if cx.opportunistic_encryption.is_enabled() && protocol.is_encrypted() {
298 cx.transport_state()
299 .await
300 .initiate_connection(self.config.ip, protocol);
301 } else if cx.opportunistic_encryption.is_enabled() && !protocol.is_encrypted() {
302 self.consider_probe_encrypted_transport(&policy, cx).await;
303 }
304
305 let handle_fut = self
307 .connection_provider
308 .new_connection(self.config.ip, config, cx)
309 .map_err(|e| (e, Some(protocol)))?;
310
311 let handle = Box::pin(handle_fut)
312 .await
313 .map_err(|e| (e, Some(protocol)))?;
314
315 if cx.opportunistic_encryption.is_enabled() && protocol.is_encrypted() {
316 cx.transport_state()
317 .await
318 .complete_connection(self.config.ip, protocol);
319 }
320
321 let state = ConnectionState::new(handle.clone(), protocol);
323 let meta = state.meta.clone();
324 self.connections.lock().await.push(state);
325 Ok(ConnectedClient {
326 handle,
327 meta,
328 protocol,
329 reuse: ConnectionReuse::Fresh,
330 })
331 }
332
333 pub(super) fn protocols(&self) -> impl Iterator<Item = Protocol> + '_ {
334 self.config
335 .connections
336 .iter()
337 .map(|conn| conn.protocol.to_protocol())
338 }
339
340 pub(super) fn ip(&self) -> IpAddr {
341 self.config.ip
342 }
343
344 pub(crate) fn decayed_srtt(&self) -> f64 {
345 self.server_srtt.current()
346 }
347
348 pub(super) fn record_cancelled(&self, winner_rtt: Duration) {
362 const CANCEL_PENALTY: Duration = Duration::from_millis(5);
363 self.server_srtt.record(winner_rtt + CANCEL_PENALTY);
364 }
365
366 #[cfg(test)]
367 pub(crate) fn test_record_failure(&self) {
368 self.server_srtt.record_failure();
369 }
370
371 #[cfg(test)]
372 #[allow(dead_code)]
373 pub(crate) fn is_connected(&self) -> bool {
374 let Some(connections) = self.connections.try_lock() else {
375 return true;
377 };
378
379 connections.iter().any(|conn| match conn.meta.status() {
380 Status::Established | Status::Init => true,
381 Status::Failed => false,
382 })
383 }
384
385 pub(crate) fn trust_negative_responses(&self) -> bool {
386 self.config.trust_negative_responses
387 }
388
389 async fn consider_probe_encrypted_transport(
390 &self,
391 policy: &ConnectionPolicy,
392 cx: &Arc<PoolContext>,
393 ) {
394 let Some(probe_config) =
395 policy.select_encrypted_connection_config(&self.config.connections)
396 else {
397 warn!("no encrypted connection configs available for probing");
398 return;
399 };
400
401 let probe_protocol = probe_config.protocol.to_protocol();
402 let should_probe = {
403 let state = cx.transport_state().await;
404 state.should_probe_encrypted(
405 self.config.ip,
406 probe_protocol,
407 &cx.opportunistic_encryption,
408 )
409 };
410
411 if !should_probe {
412 return;
413 }
414
415 if let Err(err) = self.probe_encrypted_transport(cx, probe_config) {
416 error!(%err, "opportunistic encrypted probe attempt failed");
417 }
418 }
419
420 fn probe_encrypted_transport(
421 &self,
422 cx: &Arc<PoolContext>,
423 probe_config: &ConnectionConfig,
424 ) -> Result<(), NetError> {
425 let mut budget = cx.opportunistic_probe_budget.load(Ordering::Relaxed);
426 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
427 self.opportunistic_probe_metrics.probe_budget.set(budget);
428 loop {
429 if budget == 0 {
430 debug!("no remaining budget for opportunistic probing");
431 return Ok(());
432 }
433 match cx.opportunistic_probe_budget.compare_exchange_weak(
434 budget,
435 budget - 1,
436 Ordering::AcqRel,
437 Ordering::Relaxed,
438 ) {
439 Ok(_) => break,
440 Err(current) => budget = current,
441 }
442 }
443
444 let connect = ProbeRequest::new(
445 probe_config,
446 self,
447 cx,
448 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
449 self.opportunistic_probe_metrics.clone(),
450 )?;
451 self.connection_provider
452 .runtime_provider()
453 .create_handle()
454 .spawn_bg(connect.run());
455
456 Ok(())
457 }
458}
459
460struct ProbeRequest<P: ConnectionProvider> {
461 ip: IpAddr,
462 proto: Protocol,
463 connecting: P::FutureConn,
464 context: Arc<PoolContext>,
465 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
466 metrics: ProbeMetrics,
467 provider: PhantomData<P>,
468}
469
470impl<P: ConnectionProvider> ProbeRequest<P> {
471 fn new(
472 config: &ConnectionConfig,
473 ns: &NameServer<P>,
474 cx: &Arc<PoolContext>,
475 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
476 metrics: ProbeMetrics,
477 ) -> Result<Self, NetError> {
478 Ok(Self {
479 ip: ns.config.ip,
480 proto: config.protocol.to_protocol(),
481 connecting: ns
482 .connection_provider
483 .new_connection(ns.config.ip, config, cx)?,
484 context: cx.clone(),
485 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
486 metrics,
487 provider: PhantomData,
488 })
489 }
490
491 async fn run(self) {
492 let Self {
493 ip,
494 proto,
495 connecting,
496 context,
497 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
498 metrics,
499 provider: _,
500 } = self;
501
502 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
503 let start = Instant::now();
504
505 context
506 .transport_state()
507 .await
508 .initiate_connection(ip, proto);
509 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
510 metrics.increment_attempts(proto);
511
512 let conn = match connecting.await {
513 Ok(conn) => conn,
514 Err(err) => {
515 debug!(?proto, "probe connection failed");
516 let _prev = context
517 .opportunistic_probe_budget
518 .fetch_add(1, Ordering::Relaxed);
519 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
520 {
521 metrics.increment_errors(proto, &err);
522 metrics.probe_budget.set(_prev + 1);
523 metrics.record_probe_duration(proto, start.elapsed());
524 }
525 context
526 .transport_state()
527 .await
528 .error_received(ip, proto, &err);
529 return;
530 }
531 };
532
533 debug!(?proto, "probe connection succeeded");
534 context
535 .transport_state()
536 .await
537 .complete_connection(ip, proto);
538
539 match conn
540 .send(DnsRequest::from_query(
541 Query::query(Name::root(), RecordType::NS),
542 DnsRequestOptions::default(),
543 ))
544 .first_answer()
545 .await
546 {
547 Ok(_) => {
548 debug!(?proto, "probe query succeeded");
549 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
550 metrics.increment_successes(proto);
551 context.transport_state().await.response_received(ip, proto);
552 }
553 Err(err) => {
554 debug!(?proto, ?err, "probe query failed");
555 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
556 metrics.increment_errors(proto, &err);
557 context
558 .transport_state()
559 .await
560 .error_received(ip, proto, &err);
561 }
562 }
563
564 let _prev = context
565 .opportunistic_probe_budget
566 .fetch_add(1, Ordering::Relaxed);
567 #[cfg(all(feature = "metrics", any(feature = "__tls", feature = "__quic")))]
568 {
569 metrics.probe_budget.set(_prev + 1);
570 metrics.record_probe_duration(proto, start.elapsed());
571 }
572 }
573}
574
575#[derive(Clone, Copy, PartialEq, Eq)]
577enum ConnectionReuse {
578 Reused,
579 Fresh,
580}
581
582struct ConnectedClient<P: ConnectionProvider> {
584 handle: P::Conn,
585 meta: Arc<ConnectionMeta>,
586 protocol: Protocol,
587 reuse: ConnectionReuse,
588}
589
590struct ConnectionState<P: ConnectionProvider> {
591 protocol: Protocol,
592 handle: P::Conn,
593 meta: Arc<ConnectionMeta>,
594}
595
596impl<P: ConnectionProvider> ConnectionState<P> {
597 fn new(handle: P::Conn, protocol: Protocol) -> Self {
598 Self {
599 protocol,
600 handle,
601 meta: Arc::new(ConnectionMeta::default()),
602 }
603 }
604}
605
606struct ConnectionMeta {
607 status: AtomicU8,
608 srtt: DecayingSrtt,
609}
610
611impl ConnectionMeta {
612 fn set_status(&self, status: Status) {
613 self.status.store(status.into(), Ordering::Release);
614 }
615
616 fn status(&self) -> Status {
617 Status::from(self.status.load(Ordering::Acquire))
618 }
619}
620
621impl Default for ConnectionMeta {
622 fn default() -> Self {
623 Self {
627 status: AtomicU8::new(Status::Init.into()),
628 srtt: DecayingSrtt::new(Duration::from_micros(rand::random_range(1..32))),
629 }
630 }
631}
632
633struct DecayingSrtt {
634 srtt_microseconds: AtomicU32,
662
663 last_update: SyncMutex<Option<Instant>>,
665}
666
667impl DecayingSrtt {
668 fn new(initial_srtt: Duration) -> Self {
669 Self {
670 srtt_microseconds: AtomicU32::new(initial_srtt.as_micros() as u32),
671 last_update: SyncMutex::new(None),
672 }
673 }
674
675 fn record(&self, rtt: Duration) {
676 self.update(
681 rtt.as_micros() as u32,
682 |cur_srtt_microseconds, last_update| {
683 let factor = compute_srtt_factor(last_update, 3);
687 let new_srtt = (1.0 - factor) * (rtt.as_micros() as f64)
688 + factor * f64::from(cur_srtt_microseconds);
689 new_srtt.round() as u32
690 },
691 );
692 }
693
694 fn record_failure(&self) {
696 self.update(
697 Self::FAILURE_PENALTY,
698 |cur_srtt_microseconds, _last_update| {
699 cur_srtt_microseconds.saturating_add(Self::FAILURE_PENALTY)
700 },
701 );
702 }
703
704 fn current(&self) -> f64 {
713 let srtt = f64::from(self.srtt_microseconds.load(Ordering::Acquire));
714 self.last_update.lock().map_or(srtt, |last_update| {
715 srtt * compute_srtt_factor(last_update, 180)
726 })
727 }
728
729 fn update(&self, default: u32, update_fn: impl Fn(u32, Instant) -> u32) {
735 let last_update = self.last_update.lock().replace(Instant::now());
736 let _ = self.srtt_microseconds.fetch_update(
737 Ordering::SeqCst,
738 Ordering::SeqCst,
739 move |cur_srtt_microseconds| {
740 Some(
741 last_update
742 .map_or(default, |last_update| {
743 update_fn(cur_srtt_microseconds, last_update)
744 })
745 .min(Self::MAX_SRTT_MICROS),
746 )
747 },
748 );
749 }
750
751 #[cfg(all(test, feature = "tokio"))]
755 fn as_duration(&self) -> Duration {
756 Duration::from_micros(u64::from(self.srtt_microseconds.load(Ordering::Acquire)))
757 }
758
759 const FAILURE_PENALTY: u32 = Duration::from_millis(150).as_micros() as u32;
760 const MAX_SRTT_MICROS: u32 = Duration::from_secs(5).as_micros() as u32;
761}
762
763fn compute_srtt_factor(last_update: Instant, weight: u32) -> f64 {
772 let exponent = (-last_update.elapsed().as_secs_f64().max(1.0)) / f64::from(weight);
773 exponent.exp()
774}
775
776#[derive(Debug, Eq, PartialEq, Copy, Clone)]
778#[repr(u8)]
779enum Status {
780 Failed = 0,
786 Init = 1,
788 Established = 2,
791}
792
793impl From<Status> for u8 {
794 fn from(val: Status) -> Self {
796 val as Self
797 }
798}
799
800impl From<u8> for Status {
801 fn from(val: u8) -> Self {
802 match val {
803 2 => Self::Established,
804 1 => Self::Init,
805 _ => Self::Failed,
806 }
807 }
808}
809
810#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
811pub(crate) struct ConnectionPolicy {
812 pub(crate) disable_udp: bool,
813}
814
815impl ConnectionPolicy {
816 pub(crate) fn allows_server<P: ConnectionProvider>(&self, server: &NameServer<P>) -> bool {
818 server.protocols().any(|p| self.allows_protocol(p))
819 }
820
821 fn select_connection<'a, P: ConnectionProvider>(
826 &self,
827 ip: IpAddr,
828 encrypted_transport_state: &NameServerTransportState,
829 opportunistic_encryption: &OpportunisticEncryption,
830 connections: &'a [ConnectionState<P>],
831 ) -> Option<&'a ConnectionState<P>> {
832 let selected = connections
833 .iter()
834 .filter(|conn| self.allows_protocol(conn.protocol))
835 .min_by(|a, b| self.compare_connections(opportunistic_encryption.is_enabled(), a, b));
836
837 let selected = selected?;
838
839 match opportunistic_encryption.is_enabled()
845 && !selected.protocol.is_encrypted()
846 && encrypted_transport_state.any_recent_success(ip, opportunistic_encryption)
847 {
848 true => None,
849 false => Some(selected),
850 }
851 }
852
853 fn select_connection_config<'a>(
858 &self,
859 ip: IpAddr,
860 encrypted_transport_state: &NameServerTransportState,
861 opportunistic_encryption: &OpportunisticEncryption,
862 connection_configs: &'a [ConnectionConfig],
863 ) -> Option<&'a ConnectionConfig> {
864 connection_configs
865 .iter()
866 .filter(|c| self.allows_protocol(c.protocol.to_protocol()))
867 .min_by(|a, b| {
868 self.compare_connection_configs(
869 ip,
870 encrypted_transport_state,
871 opportunistic_encryption,
872 a,
873 b,
874 )
875 })
876 }
877
878 fn select_encrypted_connection_config<'a>(
880 &self,
881 connection_config: &'a [ConnectionConfig],
882 ) -> Option<&'a ConnectionConfig> {
883 connection_config
884 .iter()
885 .filter(|c| self.allows_protocol(c.protocol.to_protocol()))
886 .find(|c| c.protocol.to_protocol().is_encrypted())
887 }
888
889 fn allows_protocol(&self, protocol: Protocol) -> bool {
891 !(self.disable_udp && protocol == Protocol::Udp)
892 }
893
894 fn compare_connections<P: ConnectionProvider>(
897 &self,
898 opportunistic_encryption: bool,
899 a: &ConnectionState<P>,
900 b: &ConnectionState<P>,
901 ) -> cmp::Ordering {
902 if opportunistic_encryption {
905 match (a.protocol.is_encrypted(), b.protocol.is_encrypted()) {
906 (true, false) => return cmp::Ordering::Less,
907 (false, true) => return cmp::Ordering::Greater,
908 _ => {}
910 }
911 }
912
913 match (a.protocol, b.protocol) {
914 (ap, bp) if ap == bp => a.meta.srtt.current().total_cmp(&b.meta.srtt.current()),
915 (Protocol::Udp, _) => cmp::Ordering::Less,
916 (_, Protocol::Udp) => cmp::Ordering::Greater,
917 _ => a.meta.srtt.current().total_cmp(&b.meta.srtt.current()),
918 }
919 }
920
921 fn compare_connection_configs(
922 &self,
923 ip: IpAddr,
924 encrypted_transport_state: &NameServerTransportState,
925 opportunistic_encryption: &OpportunisticEncryption,
926 a: &ConnectionConfig,
927 b: &ConnectionConfig,
928 ) -> cmp::Ordering {
929 let a_protocol = a.protocol.to_protocol();
930 let b_protocol = b.protocol.to_protocol();
931
932 if opportunistic_encryption.is_enabled() {
935 let a_recent_enc_success = a_protocol.is_encrypted()
936 && encrypted_transport_state.recent_success(
937 ip,
938 a_protocol,
939 opportunistic_encryption,
940 );
941 let b_recent_enc_success = b_protocol.is_encrypted()
942 && encrypted_transport_state.recent_success(
943 ip,
944 b_protocol,
945 opportunistic_encryption,
946 );
947
948 match (a_recent_enc_success, b_recent_enc_success) {
949 (true, false) => return cmp::Ordering::Less,
950 (false, true) => return cmp::Ordering::Greater,
951 _ => {}
953 }
954 }
955
956 match (a_protocol, b_protocol) {
958 (ap, bp) if ap == bp => cmp::Ordering::Equal,
959 (Protocol::Udp, _) => cmp::Ordering::Less,
960 (_, Protocol::Udp) => cmp::Ordering::Greater,
961 _ => cmp::Ordering::Equal,
962 }
963 }
964}
965
966#[cfg(all(test, feature = "tokio"))]
967mod tests {
968 use std::cmp;
969 use std::net::{IpAddr, Ipv4Addr};
970 use std::str::FromStr;
971 use std::time::Duration;
972
973 use test_support::subscribe;
974 use tokio::net::UdpSocket;
975 use tokio::spawn;
976
977 use super::*;
978 use crate::config::{ConnectionConfig, ProtocolConfig};
979 use crate::connection_provider::TlsConfig;
980 use crate::net::runtime::TokioRuntimeProvider;
981 use crate::proto::op::{DnsRequest, DnsRequestOptions, Message, Query, ResponseCode};
982 use crate::proto::rr::rdata::NULL;
983 use crate::proto::rr::{Name, RData, Record, RecordType};
984
985 #[tokio::test]
986 async fn test_name_server() {
987 subscribe();
988
989 let options = ResolverOpts::default();
990 let config = NameServerConfig::udp(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)));
991 let name_server = Arc::new(NameServer::new(
992 [].into_iter(),
993 config,
994 &options,
995 TokioRuntimeProvider::default(),
996 ));
997
998 let cx = Arc::new(PoolContext::new(options, TlsConfig::new().unwrap()));
999 let name = Name::parse("www.example.com.", None).unwrap();
1000 let response = name_server
1001 .send(
1002 DnsRequest::from_query(
1003 Query::query(name.clone(), RecordType::A),
1004 DnsRequestOptions::default(),
1005 ),
1006 ConnectionPolicy::default(),
1007 &cx,
1008 )
1009 .await
1010 .expect("query failed");
1011 assert_eq!(response.response_code, ResponseCode::NoError);
1012 }
1013
1014 #[tokio::test]
1015 async fn test_failed_name_server() {
1016 subscribe();
1017
1018 let options = ResolverOpts {
1019 timeout: Duration::from_millis(1), ..ResolverOpts::default()
1021 };
1022
1023 let config = NameServerConfig::udp(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 252)));
1024 let name_server = Arc::new(NameServer::new(
1025 [],
1026 config,
1027 &options,
1028 TokioRuntimeProvider::default(),
1029 ));
1030
1031 let cx = Arc::new(PoolContext::new(options, TlsConfig::new().unwrap()));
1032 let name = Name::parse("www.example.com.", None).unwrap();
1033 assert!(
1034 name_server
1035 .send(
1036 DnsRequest::from_query(
1037 Query::query(name.clone(), RecordType::A),
1038 DnsRequestOptions::default(),
1039 ),
1040 ConnectionPolicy::default(),
1041 &cx
1042 )
1043 .await
1044 .is_err()
1045 );
1046 }
1047
1048 #[tokio::test]
1049 async fn case_randomization_query_preserved() {
1050 subscribe();
1051
1052 let provider = TokioRuntimeProvider::default();
1053 let server = UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)).await.unwrap();
1054 let server_addr = server.local_addr().unwrap();
1055 let name = Name::from_str("dead.beef.").unwrap();
1056 let data = b"DEADBEEF";
1057
1058 spawn({
1059 let name = name.clone();
1060 async move {
1061 let mut buffer = [0_u8; 512];
1062 let (len, addr) = server.recv_from(&mut buffer).await.unwrap();
1063 let request = Message::from_vec(&buffer[0..len]).unwrap();
1064 let mut response = Message::response(request.id, request.op_code);
1065 response.add_queries(request.queries.to_vec());
1066 response.add_answer(Record::from_rdata(
1067 name,
1068 0,
1069 RData::NULL(NULL::with(data.to_vec())),
1070 ));
1071 let response_buffer = response.to_vec().unwrap();
1072 server.send_to(&response_buffer, addr).await.unwrap();
1073 }
1074 });
1075
1076 let config = NameServerConfig {
1077 ip: server_addr.ip(),
1078 trust_negative_responses: true,
1079 connections: vec![ConnectionConfig {
1080 port: server_addr.port(),
1081 protocol: ProtocolConfig::Udp,
1082 bind_addr: None,
1083 }],
1084 };
1085
1086 let resolver_opts = ResolverOpts {
1087 case_randomization: true,
1088 ..Default::default()
1089 };
1090
1091 let cx = Arc::new(PoolContext::new(resolver_opts, TlsConfig::new().unwrap()));
1092 let mut request_options = DnsRequestOptions::default();
1093 request_options.case_randomization = true;
1094 let ns = Arc::new(NameServer::new([], config, &cx.options, provider));
1095 let response = ns
1096 .send(
1097 DnsRequest::from_query(
1098 Query::query(name.clone(), RecordType::NULL),
1099 request_options,
1100 ),
1101 ConnectionPolicy::default(),
1102 &cx,
1103 )
1104 .await
1105 .unwrap();
1106
1107 let response_query_name = response.queries.first().unwrap().name();
1108 assert!(response_query_name.eq_case(&name));
1109 }
1110
1111 #[allow(clippy::extra_unused_type_parameters)]
1112 fn is_send_sync<S: Sync + Send>() -> bool {
1113 true
1114 }
1115
1116 #[test]
1117 fn stats_are_sync() {
1118 assert!(is_send_sync::<ConnectionMeta>());
1119 }
1120
1121 #[tokio::test(start_paused = true)]
1122 async fn test_stats_cmp() {
1123 use std::cmp::Ordering;
1124 let srtt_a = DecayingSrtt::new(Duration::from_micros(10));
1125 let srtt_b = DecayingSrtt::new(Duration::from_micros(20));
1126
1127 assert_eq!(cmp(&srtt_a, &srtt_b), Ordering::Less);
1130
1131 srtt_a.record(Duration::from_millis(30));
1133 tokio::time::advance(Duration::from_secs(5)).await;
1134 assert_eq!(cmp(&srtt_a, &srtt_b), Ordering::Greater);
1135
1136 srtt_b.record(Duration::from_millis(50));
1139 tokio::time::advance(Duration::from_secs(5)).await;
1140 assert_eq!(cmp(&srtt_a, &srtt_b), Ordering::Less);
1141
1142 srtt_a.record_failure();
1145 tokio::time::advance(Duration::from_secs(5)).await;
1146 assert_eq!(cmp(&srtt_a, &srtt_b), Ordering::Greater);
1147
1148 while cmp(&srtt_a, &srtt_b) != Ordering::Less {
1150 srtt_b.record(Duration::from_millis(50));
1151 tokio::time::advance(Duration::from_secs(5)).await;
1152 }
1153
1154 srtt_a.record(Duration::from_millis(30));
1155 tokio::time::advance(Duration::from_secs(3)).await;
1156 assert_eq!(cmp(&srtt_a, &srtt_b), Ordering::Less);
1157 }
1158
1159 fn cmp(a: &DecayingSrtt, b: &DecayingSrtt) -> cmp::Ordering {
1160 a.current().total_cmp(&b.current())
1161 }
1162
1163 #[tokio::test(start_paused = true)]
1164 async fn test_record_rtt() {
1165 let srtt = DecayingSrtt::new(Duration::from_micros(10));
1166
1167 let first_rtt = Duration::from_millis(50);
1168 srtt.record(first_rtt);
1169
1170 assert_eq!(srtt.as_duration(), first_rtt);
1172
1173 tokio::time::advance(Duration::from_secs(3)).await;
1174
1175 srtt.record(Duration::from_millis(100));
1177 assert_eq!(srtt.as_duration(), Duration::from_micros(81606));
1178 }
1179
1180 #[test]
1181 fn test_record_rtt_maximum_value() {
1182 let srtt = DecayingSrtt::new(Duration::from_micros(10));
1183
1184 srtt.record(Duration::MAX);
1185 assert_eq!(
1187 srtt.as_duration(),
1188 Duration::from_micros(DecayingSrtt::MAX_SRTT_MICROS.into())
1189 );
1190 }
1191
1192 #[tokio::test(start_paused = true)]
1193 async fn test_record_connection_failure() {
1194 let srtt = DecayingSrtt::new(Duration::from_micros(10));
1195
1196 for failure_count in 1..4 {
1199 srtt.record_failure();
1200 assert_eq!(
1201 srtt.as_duration(),
1202 Duration::from_micros(
1203 DecayingSrtt::FAILURE_PENALTY
1204 .checked_mul(failure_count)
1205 .expect("checked_mul overflow")
1206 .into()
1207 )
1208 );
1209 tokio::time::advance(Duration::from_secs(3)).await;
1210 }
1211
1212 srtt.record(Duration::from_millis(50));
1215 assert_eq!(srtt.as_duration(), Duration::from_micros(197152));
1216 }
1217
1218 #[test]
1219 fn test_record_connection_failure_maximum_value() {
1220 let srtt = DecayingSrtt::new(Duration::from_micros(10));
1221
1222 let num_failures = (DecayingSrtt::MAX_SRTT_MICROS / DecayingSrtt::FAILURE_PENALTY) + 1;
1223 for _ in 0..num_failures {
1224 srtt.record_failure();
1225 }
1226
1227 assert_eq!(
1229 srtt.as_duration(),
1230 Duration::from_micros(DecayingSrtt::MAX_SRTT_MICROS.into())
1231 );
1232 }
1233
1234 #[tokio::test(start_paused = true)]
1235 async fn test_decayed_srtt() {
1236 let initial_srtt = 10;
1237 let srtt = DecayingSrtt::new(Duration::from_micros(initial_srtt));
1238
1239 assert_eq!(srtt.current() as u32, initial_srtt as u32);
1241
1242 tokio::time::advance(Duration::from_secs(5)).await;
1243 srtt.record(Duration::from_millis(100));
1244
1245 tokio::time::advance(Duration::from_millis(500)).await;
1248 assert_eq!(srtt.current() as u32, 99445);
1249
1250 tokio::time::advance(Duration::from_secs(5)).await;
1251 assert_eq!(srtt.current() as u32, 96990);
1252 }
1253}
1254
1255#[cfg(all(test, feature = "__tls"))]
1256mod opportunistic_enc_tests {
1257 use std::io;
1258 use std::net::{IpAddr, Ipv4Addr};
1259 use std::sync::Arc;
1260 use std::time::{Duration, SystemTime};
1261
1262 #[cfg(feature = "metrics")]
1263 use metrics::{Label, Unit, with_local_recorder};
1264 #[cfg(feature = "metrics")]
1265 use metrics_util::debugging::DebuggingRecorder;
1266 use mock_provider::{MockClientHandle, MockProvider};
1267 use test_support::subscribe;
1268 #[cfg(feature = "metrics")]
1269 use test_support::{assert_counter_eq, assert_gauge_eq, assert_histogram_sample_count_eq};
1270
1271 use crate::config::{
1272 NameServerConfig, OpportunisticEncryption, OpportunisticEncryptionConfig, ProtocolConfig,
1273 ResolverOpts,
1274 };
1275 use crate::connection_provider::TlsConfig;
1276 #[cfg(feature = "metrics")]
1277 use crate::metrics::opportunistic_encryption::{
1278 PROBE_ATTEMPTS_TOTAL, PROBE_BUDGET_TOTAL, PROBE_DURATION_SECONDS, PROBE_ERRORS_TOTAL,
1279 PROBE_SUCCESSES_TOTAL, PROBE_TIMEOUTS_TOTAL,
1280 };
1281 use crate::name_server::{ConnectionPolicy, ConnectionState, NameServer, mock_provider};
1282 use crate::name_server_pool::{NameServerTransportState, PoolContext};
1283 use crate::net::NetError;
1284 use crate::net::xfer::Protocol;
1285
1286 #[tokio::test]
1287 async fn test_select_connection_opportunistic_enc_disabled() {
1288 let mut policy = ConnectionPolicy::default();
1289 let connections = vec![
1290 mock_connection(Protocol::Udp),
1291 mock_connection(Protocol::Tcp),
1292 ];
1293
1294 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1295 let state = NameServerTransportState::default();
1296 let opp_enc = OpportunisticEncryption::Disabled;
1297
1298 let selected = policy.select_connection(ns_ip, &state, &opp_enc, &connections);
1301 assert!(selected.is_some());
1302 assert_eq!(selected.unwrap().protocol, Protocol::Udp);
1303
1304 policy.disable_udp = true;
1307 let selected = policy.select_connection(ns_ip, &state, &opp_enc, &connections);
1308 assert!(selected.is_some());
1309 assert_eq!(selected.unwrap().protocol, Protocol::Tcp);
1310 }
1311
1312 #[tokio::test]
1313 async fn test_select_connection_opportunistic_enc_enabled() {
1314 let policy = ConnectionPolicy::default();
1315 let connections = [
1316 mock_connection(Protocol::Udp),
1317 mock_connection(Protocol::Tcp),
1318 mock_connection(Protocol::Tls),
1320 ];
1321
1322 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1323 let state = NameServerTransportState::default();
1324 let opp_enc = &OpportunisticEncryption::Enabled {
1325 config: OpportunisticEncryptionConfig::default(),
1326 };
1327
1328 let selected = policy.select_connection(ns_ip, &state, opp_enc, &connections);
1331 assert!(selected.is_some());
1332 assert_eq!(selected.unwrap().protocol, Protocol::Tls);
1333 }
1334
1335 #[tokio::test]
1336 async fn test_select_connection_opportunistic_enc_enabled_no_state() {
1337 let mut policy = ConnectionPolicy::default();
1338 let connections = [
1339 mock_connection(Protocol::Udp),
1340 mock_connection(Protocol::Tcp),
1341 ];
1343
1344 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1345 let state = NameServerTransportState::default();
1346 let opp_enc = &OpportunisticEncryption::Enabled {
1347 config: OpportunisticEncryptionConfig::default(),
1348 };
1349
1350 let selected = policy.select_connection(ns_ip, &state, opp_enc, &connections);
1353 assert!(selected.is_some());
1354 assert_eq!(selected.unwrap().protocol, Protocol::Udp);
1355
1356 policy.disable_udp = true;
1359 let selected = policy.select_connection(ns_ip, &state, opp_enc, &connections);
1360 assert!(selected.is_some());
1361 assert_eq!(selected.unwrap().protocol, Protocol::Tcp);
1362 }
1363
1364 #[tokio::test]
1365 async fn test_select_connection_opportunistic_enc_enabled_failed_probe() {
1366 let policy = ConnectionPolicy::default();
1367 let connections = [
1368 mock_connection(Protocol::Udp),
1369 mock_connection(Protocol::Tcp),
1370 ];
1372
1373 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1374 let mut state = NameServerTransportState::default();
1375 let opp_enc = &OpportunisticEncryption::Enabled {
1376 config: OpportunisticEncryptionConfig::default(),
1377 };
1378
1379 state.error_received(
1381 ns_ip,
1382 Protocol::Tls,
1383 &NetError::from(io::Error::new(
1384 io::ErrorKind::ConnectionRefused,
1385 "nameserver refused TLS connection",
1386 )),
1387 );
1388
1389 let selected = policy.select_connection(ns_ip, &state, opp_enc, &connections);
1392 assert!(selected.is_some());
1393 assert_eq!(selected.unwrap().protocol, Protocol::Udp);
1394 }
1395
1396 #[tokio::test]
1397 async fn test_select_connection_opportunistic_enc_enabled_in_progress_probe() {
1398 let policy = ConnectionPolicy::default();
1399 let connections = [
1400 mock_connection(Protocol::Udp),
1401 mock_connection(Protocol::Tcp),
1402 ];
1404
1405 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1406 let mut state = NameServerTransportState::default();
1407 let opp_enc = &OpportunisticEncryption::Enabled {
1408 config: OpportunisticEncryptionConfig::default(),
1409 };
1410
1411 state.initiate_connection(ns_ip, Protocol::Tls);
1413
1414 let selected = policy.select_connection(ns_ip, &state, opp_enc, &connections);
1417 assert!(selected.is_some());
1418 assert_eq!(selected.unwrap().protocol, Protocol::Udp);
1419
1420 state.complete_connection(ns_ip, Protocol::Tls);
1423
1424 let selected = policy.select_connection(ns_ip, &state, opp_enc, &connections);
1426 assert!(selected.is_some());
1427 assert_eq!(selected.unwrap().protocol, Protocol::Udp);
1428 }
1429
1430 #[tokio::test]
1431 async fn test_select_connection_opportunistic_enc_enabled_stale_probe() {
1432 let policy = ConnectionPolicy::default();
1433 let connections = [
1434 mock_connection(Protocol::Udp),
1435 mock_connection(Protocol::Tcp),
1436 ];
1438
1439 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1440 let mut state = NameServerTransportState::default();
1441 let opp_enc_config = OpportunisticEncryptionConfig {
1442 persistence_period: Duration::from_secs(10),
1443 ..OpportunisticEncryptionConfig::default()
1444 };
1445 let opp_enc = &OpportunisticEncryption::Enabled {
1446 config: opp_enc_config.clone(),
1447 };
1448
1449 state.complete_connection(ns_ip, Protocol::Tls);
1451 state.response_received(ns_ip, Protocol::Tls);
1452 let stale_time =
1454 SystemTime::now() - opp_enc_config.persistence_period - Duration::from_secs(1);
1455 state.set_last_response(ns_ip, Protocol::Tls, stale_time);
1456
1457 let selected = policy.select_connection(ns_ip, &state, opp_enc, &connections);
1461 assert!(selected.is_some());
1462 assert_eq!(selected.unwrap().protocol, Protocol::Udp);
1463 }
1464
1465 #[tokio::test]
1466 async fn test_select_connection_opportunistic_enc_enabled_good_probe() {
1467 let policy = ConnectionPolicy::default();
1468 let connections = [
1469 mock_connection(Protocol::Udp),
1470 mock_connection(Protocol::Tcp),
1471 ];
1473
1474 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1475 let mut state = NameServerTransportState::default();
1476 let opp_enc = &OpportunisticEncryption::Enabled {
1477 config: OpportunisticEncryptionConfig::default(),
1478 };
1479
1480 state.complete_connection(ns_ip, Protocol::Tls);
1483 state.response_received(ns_ip, Protocol::Tls);
1484
1485 let selected = policy.select_connection(ns_ip, &state, opp_enc, &connections);
1489 assert!(selected.is_none());
1490 }
1491
1492 #[tokio::test]
1493 async fn test_select_connection_config_opportunistic_enc_disabled() {
1494 let mut policy = ConnectionPolicy::default();
1495
1496 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1497 let configs = NameServerConfig::opportunistic_encryption(ns_ip).connections;
1498
1499 let state = NameServerTransportState::default();
1500 let opp_enc = OpportunisticEncryption::Disabled;
1501
1502 let selected = policy.select_connection_config(ns_ip, &state, &opp_enc, &configs);
1505 assert!(selected.is_some());
1506 assert_eq!(selected.unwrap().protocol, ProtocolConfig::Udp);
1507
1508 policy.disable_udp = true;
1511 let selected = policy.select_connection_config(ns_ip, &state, &opp_enc, &configs);
1512 assert!(selected.is_some());
1513 assert_eq!(selected.unwrap().protocol, ProtocolConfig::Tcp);
1514 }
1515
1516 #[tokio::test]
1517 async fn test_select_connection_config_opportunistic_enc_enabled_no_state() {
1518 let mut policy = ConnectionPolicy::default();
1519 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1520 let configs = NameServerConfig::opportunistic_encryption(ns_ip).connections;
1521
1522 let state = NameServerTransportState::default();
1523 let opp_enc = &OpportunisticEncryption::Enabled {
1524 config: OpportunisticEncryptionConfig::default(),
1525 };
1526
1527 let selected = policy.select_connection_config(ns_ip, &state, opp_enc, &configs);
1530 assert!(selected.is_some());
1531 assert_eq!(selected.unwrap().protocol, ProtocolConfig::Udp);
1532
1533 policy.disable_udp = true;
1536 let selected = policy.select_connection_config(ns_ip, &state, opp_enc, &configs);
1537 assert!(selected.is_some());
1538 assert_eq!(selected.unwrap().protocol, ProtocolConfig::Tcp);
1539 }
1540
1541 #[tokio::test]
1542 async fn test_select_connection_config_opportunistic_enc_enabled_failed_probe() {
1543 let policy = ConnectionPolicy::default();
1544 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1545 let configs = NameServerConfig::opportunistic_encryption(ns_ip).connections;
1546
1547 let mut state = NameServerTransportState::default();
1548 let opp_enc = &OpportunisticEncryption::Enabled {
1549 config: OpportunisticEncryptionConfig::default(),
1550 };
1551
1552 state.error_received(
1554 ns_ip,
1555 Protocol::Tls,
1556 &NetError::from(io::Error::new(
1557 io::ErrorKind::ConnectionRefused,
1558 "nameserver refused TLS connection",
1559 )),
1560 );
1561
1562 let selected = policy.select_connection_config(ns_ip, &state, opp_enc, &configs);
1565 assert!(selected.is_some());
1566 assert_eq!(selected.unwrap().protocol, ProtocolConfig::Udp);
1567 }
1568
1569 #[tokio::test]
1570 async fn test_select_connection_config_opportunistic_enc_enabled_stale_probe() {
1571 let policy = ConnectionPolicy::default();
1572 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1573 let configs = NameServerConfig::opportunistic_encryption(ns_ip).connections;
1574
1575 let mut state = NameServerTransportState::default();
1576 let opp_enc_config = OpportunisticEncryptionConfig {
1577 persistence_period: Duration::from_secs(10),
1578 ..OpportunisticEncryptionConfig::default()
1579 };
1580 let opp_enc = &OpportunisticEncryption::Enabled {
1581 config: opp_enc_config.clone(),
1582 };
1583
1584 state.complete_connection(ns_ip, Protocol::Tls);
1586 state.response_received(ns_ip, Protocol::Tls);
1587 let stale_time =
1589 SystemTime::now() - opp_enc_config.persistence_period - Duration::from_secs(1);
1590 state.set_last_response(ns_ip, Protocol::Tls, stale_time);
1591
1592 let selected = policy.select_connection_config(ns_ip, &state, opp_enc, &configs);
1595 assert!(selected.is_some());
1596 assert_eq!(selected.unwrap().protocol, ProtocolConfig::Udp);
1597 }
1598
1599 #[tokio::test]
1600 async fn test_select_connection_config_opportunistic_enc_enabled_good_probe() {
1601 let policy = ConnectionPolicy::default();
1602 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1603 let configs = NameServerConfig::opportunistic_encryption(ns_ip).connections;
1604
1605 let mut state = NameServerTransportState::default();
1606 let opp_enc = &OpportunisticEncryption::Enabled {
1607 config: OpportunisticEncryptionConfig::default(),
1608 };
1609
1610 state.complete_connection(ns_ip, Protocol::Tls);
1613 state.response_received(ns_ip, Protocol::Tls);
1614
1615 let selected = policy.select_connection_config(ns_ip, &state, opp_enc, &configs);
1618 assert!(selected.is_some());
1619 assert!(matches!(
1620 selected.unwrap().protocol,
1621 ProtocolConfig::Tls { .. }
1622 ));
1623 }
1624
1625 #[tokio::test]
1626 async fn test_opportunistic_probe() {
1627 subscribe();
1628
1629 let cx = PoolContext::new(ResolverOpts::default(), TlsConfig::new().unwrap())
1631 .with_opportunistic_encryption()
1632 .with_probe_budget(10);
1633
1634 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1635 let mock_provider = MockProvider::default();
1636 assert!(
1637 test_connected_mut_client(ns_ip, Arc::new(cx), &mock_provider)
1638 .await
1639 .is_ok()
1640 );
1641
1642 let recorded_calls = mock_provider.new_connection_calls();
1643 assert_eq!(recorded_calls.len(), 2);
1645 let (ips, protocols): (Vec<IpAddr>, Vec<ProtocolConfig>) =
1646 recorded_calls.into_iter().unzip();
1647 assert!(ips.iter().all(|ip| *ip == ns_ip));
1649 let protocols = protocols
1651 .iter()
1652 .map(ProtocolConfig::to_protocol)
1653 .collect::<Vec<_>>();
1654 assert!(protocols.contains(&Protocol::Udp));
1655 assert!(protocols.contains(&Protocol::Tls));
1656 }
1657
1658 #[tokio::test]
1659 async fn test_opportunistic_probe_skip_in_progress() {
1660 subscribe();
1661
1662 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1663 let cx = PoolContext::new(ResolverOpts::default(), TlsConfig::new().unwrap())
1664 .with_opportunistic_encryption()
1665 .with_probe_budget(10);
1666
1667 cx.transport_state()
1669 .await
1670 .initiate_connection(ns_ip, Protocol::Tls);
1671
1672 let mock_provider = MockProvider::default();
1673 assert!(
1674 test_connected_mut_client(ns_ip, Arc::new(cx), &mock_provider)
1675 .await
1676 .is_ok()
1677 );
1678
1679 let recorded_calls = mock_provider.new_connection_calls();
1680 assert_eq!(recorded_calls.len(), 1);
1682 let (ip, protocol) = &recorded_calls[0];
1683 assert_eq!(*ip, ns_ip);
1684 assert_eq!(protocol.to_protocol(), Protocol::Udp);
1685 }
1686
1687 #[tokio::test]
1688 async fn test_opportunistic_probe_skip_recent_failure() {
1689 subscribe();
1690
1691 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1692 let cx = PoolContext::new(ResolverOpts::default(), TlsConfig::new().unwrap())
1693 .with_opportunistic_encryption()
1694 .with_probe_budget(10);
1695
1696 cx.transport_state().await.error_received(
1698 ns_ip,
1699 Protocol::Tls,
1700 &NetError::from(io::Error::new(
1701 io::ErrorKind::ConnectionRefused,
1702 "connection refused",
1703 )),
1704 );
1705
1706 let mock_provider = MockProvider::default();
1707 assert!(
1708 test_connected_mut_client(ns_ip, Arc::new(cx), &mock_provider)
1709 .await
1710 .is_ok()
1711 );
1712
1713 let recorded_calls = mock_provider.new_connection_calls();
1714 assert_eq!(recorded_calls.len(), 1);
1716 let (ip, protocol) = &recorded_calls[0];
1717 assert_eq!(*ip, ns_ip);
1718 assert_eq!(protocol.to_protocol(), Protocol::Udp);
1719 }
1720
1721 #[tokio::test]
1722 async fn test_opportunistic_probe_stale_failure() {
1723 subscribe();
1724
1725 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1726 let mut cx = PoolContext::new(ResolverOpts::default(), TlsConfig::new().unwrap())
1727 .with_probe_budget(10);
1728 let opp_enc_config = OpportunisticEncryptionConfig {
1729 damping_period: Duration::from_secs(5),
1730 ..OpportunisticEncryptionConfig::default()
1731 };
1732 cx.opportunistic_encryption = OpportunisticEncryption::Enabled {
1733 config: opp_enc_config.clone(),
1734 };
1735
1736 {
1738 let mut state = cx.transport_state().await;
1739 let old_failure_time =
1740 SystemTime::now() - opp_enc_config.damping_period - Duration::from_secs(1);
1741 state.set_failure_time(ns_ip, Protocol::Tls, old_failure_time);
1742 }
1743
1744 let mock_provider = MockProvider::default();
1745 assert!(
1746 test_connected_mut_client(ns_ip, Arc::new(cx), &mock_provider)
1747 .await
1748 .is_ok()
1749 );
1750
1751 let recorded_calls = mock_provider.new_connection_calls();
1752 assert_eq!(recorded_calls.len(), 2);
1754 let protocols = recorded_calls
1755 .iter()
1756 .map(|(_, protocol)| protocol.to_protocol())
1757 .collect::<Vec<_>>();
1758 assert!(protocols.contains(&Protocol::Udp));
1759 assert!(protocols.contains(&Protocol::Tls));
1760 }
1761
1762 #[tokio::test]
1763 async fn test_opportunistic_probe_skip_no_budget() {
1764 subscribe();
1765
1766 let ns_ip = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1));
1767 let cx = PoolContext::new(ResolverOpts::default(), TlsConfig::new().unwrap())
1768 .with_opportunistic_encryption();
1769 let mock_provider = MockProvider::default();
1770 assert!(
1772 test_connected_mut_client(ns_ip, Arc::new(cx), &mock_provider)
1773 .await
1774 .is_ok()
1775 );
1776
1777 let recorded_calls = mock_provider.new_connection_calls();
1778 assert_eq!(recorded_calls.len(), 1);
1780 let (ip, protocol) = &recorded_calls[0];
1781 assert_eq!(*ip, ns_ip);
1782 assert_eq!(protocol.to_protocol(), Protocol::Udp);
1783 }
1784
1785 fn mock_connection(protocol: Protocol) -> ConnectionState<MockProvider> {
1786 ConnectionState::new(MockClientHandle::default(), protocol)
1787 }
1788
1789 #[cfg(feature = "metrics")]
1790 #[test]
1791 fn test_opportunistic_probe_metrics_success() {
1792 subscribe();
1793 let recorder = DebuggingRecorder::new();
1794 let snapshotter = recorder.snapshotter();
1795 let initial_budget = 10;
1796
1797 with_local_recorder(&recorder, || {
1798 let runtime = tokio::runtime::Builder::new_current_thread()
1799 .enable_all()
1800 .build()
1801 .unwrap();
1802
1803 runtime.block_on(async {
1804 assert!(
1805 test_connected_mut_client(
1806 IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)),
1807 Arc::new(
1808 PoolContext::new(ResolverOpts::default(), TlsConfig::new().unwrap())
1809 .with_opportunistic_encryption()
1810 .with_probe_budget(initial_budget),
1811 ),
1812 &MockProvider::default(),
1813 )
1814 .await
1815 .is_ok()
1816 );
1817 });
1818 });
1819
1820 #[allow(clippy::mutable_key_type)]
1821 let map = snapshotter.snapshot().into_hashmap();
1822
1823 let protocol = vec![Label::new("protocol", "tls")];
1825 assert_counter_eq(&map, PROBE_ATTEMPTS_TOTAL, protocol.clone(), 1);
1826 assert_histogram_sample_count_eq(
1828 &map,
1829 PROBE_DURATION_SECONDS,
1830 protocol.clone(),
1831 1,
1832 Unit::Seconds,
1833 );
1834
1835 assert_counter_eq(&map, PROBE_SUCCESSES_TOTAL, protocol.clone(), 1);
1837
1838 assert_counter_eq(&map, PROBE_ERRORS_TOTAL, protocol, 0);
1840
1841 assert_gauge_eq(&map, PROBE_BUDGET_TOTAL, vec![], initial_budget);
1843 }
1844
1845 #[cfg(feature = "metrics")]
1846 #[test]
1847 fn test_opportunistic_probe_metrics_budget_exhausted() {
1848 subscribe();
1849 let recorder = DebuggingRecorder::new();
1850 let snapshotter = recorder.snapshotter();
1851
1852 with_local_recorder(&recorder, || {
1853 let runtime = tokio::runtime::Builder::new_current_thread()
1854 .enable_all()
1855 .build()
1856 .unwrap();
1857
1858 runtime.block_on(async {
1859 assert!(
1860 test_connected_mut_client(
1861 IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)),
1862 Arc::new(
1863 PoolContext::new(ResolverOpts::default(), TlsConfig::new().unwrap())
1864 .with_opportunistic_encryption(),
1865 ),
1866 &MockProvider::default(),
1867 )
1868 .await
1869 .is_ok()
1870 );
1871 });
1872 });
1873
1874 #[allow(clippy::mutable_key_type)]
1875 let map = snapshotter.snapshot().into_hashmap();
1876
1877 assert_gauge_eq(&map, PROBE_BUDGET_TOTAL, vec![], 0);
1879
1880 let protocol = vec![Label::new("protocol", "tls")];
1882 assert_counter_eq(&map, PROBE_ATTEMPTS_TOTAL, protocol.clone(), 0);
1883 assert_histogram_sample_count_eq(&map, PROBE_DURATION_SECONDS, protocol, 0, Unit::Seconds);
1885 }
1886
1887 #[cfg(feature = "metrics")]
1888 #[test]
1889 fn test_opportunistic_probe_metrics_connection_error() {
1890 subscribe();
1891 let recorder = DebuggingRecorder::new();
1892 let snapshotter = recorder.snapshotter();
1893 let initial_budget = 10;
1894
1895 with_local_recorder(&recorder, || {
1896 let runtime = tokio::runtime::Builder::new_current_thread()
1897 .enable_all()
1898 .build()
1899 .unwrap();
1900
1901 runtime.block_on(async {
1902 let _ = test_connected_mut_client(
1903 IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)),
1904 Arc::new(
1905 PoolContext::new(ResolverOpts::default(), TlsConfig::new().unwrap())
1906 .with_opportunistic_encryption()
1907 .with_probe_budget(initial_budget),
1908 ),
1909 &MockProvider {
1911 new_connection_error: Some(NetError::from(io::Error::new(
1912 io::ErrorKind::ConnectionRefused,
1913 "connection refused",
1914 ))),
1915 ..MockProvider::default()
1916 },
1917 )
1918 .await;
1919 });
1920 });
1921
1922 #[allow(clippy::mutable_key_type)]
1923 let map = snapshotter.snapshot().into_hashmap();
1924
1925 let protocol = vec![Label::new("protocol", "tls")];
1927 assert_counter_eq(&map, PROBE_ATTEMPTS_TOTAL, protocol.clone(), 1);
1928 assert_histogram_sample_count_eq(
1930 &map,
1931 PROBE_DURATION_SECONDS,
1932 protocol.clone(),
1933 1,
1934 Unit::Seconds,
1935 );
1936
1937 assert_counter_eq(&map, PROBE_ERRORS_TOTAL, protocol.clone(), 1);
1939
1940 assert_counter_eq(&map, PROBE_SUCCESSES_TOTAL, protocol, 0);
1943
1944 assert_gauge_eq(&map, PROBE_BUDGET_TOTAL, vec![], initial_budget);
1946 }
1947
1948 #[cfg(feature = "metrics")]
1949 #[test]
1950 fn test_opportunistic_probe_metrics_connection_timeout_error() {
1951 subscribe();
1952 let recorder = DebuggingRecorder::new();
1953 let snapshotter = recorder.snapshotter();
1954 let initial_budget = 10;
1955
1956 with_local_recorder(&recorder, || {
1957 let runtime = tokio::runtime::Builder::new_current_thread()
1958 .enable_all()
1959 .build()
1960 .unwrap();
1961
1962 runtime.block_on(async {
1963 let _ = test_connected_mut_client(
1964 IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)),
1965 Arc::new(
1966 PoolContext::new(ResolverOpts::default(), TlsConfig::new().unwrap())
1967 .with_opportunistic_encryption()
1968 .with_probe_budget(initial_budget),
1969 ),
1970 &MockProvider {
1972 new_connection_error: Some(NetError::Timeout),
1973 ..MockProvider::default()
1974 },
1975 )
1976 .await;
1977 });
1978 });
1979
1980 #[allow(clippy::mutable_key_type)]
1981 let map = snapshotter.snapshot().into_hashmap();
1982
1983 let protocol = vec![Label::new("protocol", "tls")];
1985 assert_counter_eq(&map, PROBE_ATTEMPTS_TOTAL, protocol.clone(), 1);
1986 assert_histogram_sample_count_eq(
1988 &map,
1989 PROBE_DURATION_SECONDS,
1990 protocol.clone(),
1991 1,
1992 Unit::Seconds,
1993 );
1994
1995 assert_counter_eq(&map, PROBE_TIMEOUTS_TOTAL, protocol.clone(), 1);
1997
1998 assert_counter_eq(&map, PROBE_ERRORS_TOTAL, protocol.clone(), 0);
2000
2001 assert_counter_eq(&map, PROBE_SUCCESSES_TOTAL, protocol, 0);
2004
2005 assert_gauge_eq(&map, PROBE_BUDGET_TOTAL, vec![], initial_budget);
2007 }
2008
2009 async fn test_connected_mut_client(
2015 ns_ip: IpAddr,
2016 cx: Arc<PoolContext>,
2017 provider: &MockProvider,
2018 ) -> Result<(), NetError> {
2019 let name_server = NameServer::new(
2020 [],
2021 NameServerConfig::opportunistic_encryption(ns_ip),
2022 &ResolverOpts::default(),
2023 provider.clone(),
2024 );
2025
2026 match name_server
2027 .connected_mut_client(ConnectionPolicy::default(), &cx)
2028 .await
2029 {
2030 Ok(_) => Ok(()),
2031 Err((e, _)) => Err(e),
2032 }
2033 }
2034}
2035
2036#[cfg(all(test, feature = "metrics"))]
2037mod resolver_metrics_tests {
2038 use std::net::{IpAddr, Ipv4Addr};
2039
2040 use metrics::{Label, with_local_recorder};
2041 use metrics_util::debugging::DebuggingRecorder;
2042 use mock_provider::MockProvider;
2043 use test_support::assert_counter_eq;
2044 use test_support::subscribe;
2045
2046 use super::*;
2047 use crate::connection_provider::TlsConfig;
2048 use crate::metrics::OUTGOING_QUERIES_TOTAL;
2049
2050 #[test]
2051 fn test_outgoing_query_protocol_metrics_udp() {
2052 subscribe();
2053 let recorder = DebuggingRecorder::new();
2054 let snapshotter = recorder.snapshotter();
2055
2056 with_local_recorder(&recorder, || {
2057 let runtime = tokio::runtime::Builder::new_current_thread()
2058 .enable_all()
2059 .build()
2060 .unwrap();
2061
2062 runtime.block_on(async {
2063 let options = ResolverOpts {
2064 enable_per_name_server_metrics: true,
2065 ..ResolverOpts::default()
2066 };
2067 let config = NameServerConfig::udp(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)));
2068 let name_server = Arc::new(NameServer::new(
2069 [],
2070 config,
2071 &options,
2072 MockProvider::default(),
2073 ));
2074
2075 let cx = Arc::new(PoolContext::new(options, TlsConfig::new().unwrap()));
2076 let name = Name::parse("www.example.com.", None).unwrap();
2077 let _ = name_server
2078 .send(
2079 DnsRequest::from_query(
2080 Query::query(name.clone(), RecordType::A),
2081 DnsRequestOptions::default(),
2082 ),
2083 ConnectionPolicy::default(),
2084 &cx,
2085 )
2086 .await;
2087 });
2088 });
2089
2090 #[allow(clippy::mutable_key_type)]
2091 let map = snapshotter.snapshot().into_hashmap();
2092
2093 let protocol = vec![Label::new("protocol", "udp")];
2095 assert_counter_eq(&map, OUTGOING_QUERIES_TOTAL, protocol, 1);
2096 }
2097
2098 #[test]
2099 fn test_outgoing_query_protocol_metrics_tcp() {
2100 subscribe();
2101 let recorder = DebuggingRecorder::new();
2102 let snapshotter = recorder.snapshotter();
2103
2104 with_local_recorder(&recorder, || {
2105 let runtime = tokio::runtime::Builder::new_current_thread()
2106 .enable_all()
2107 .build()
2108 .unwrap();
2109
2110 runtime.block_on(async {
2111 let options = ResolverOpts::default();
2112 let config = NameServerConfig::tcp(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)));
2113 let name_server = Arc::new(NameServer::new(
2114 [],
2115 config,
2116 &options,
2117 MockProvider::default(),
2118 ));
2119
2120 let cx = Arc::new(PoolContext::new(options, TlsConfig::new().unwrap()));
2121 let name = Name::parse("www.example.com.", None).unwrap();
2122 let _ = name_server
2123 .send(
2124 DnsRequest::from_query(
2125 Query::query(name.clone(), RecordType::A),
2126 DnsRequestOptions::default(),
2127 ),
2128 ConnectionPolicy::default(),
2129 &cx,
2130 )
2131 .await;
2132 });
2133 });
2134
2135 #[allow(clippy::mutable_key_type)]
2136 let map = snapshotter.snapshot().into_hashmap();
2137
2138 let protocol = vec![Label::new("protocol", "tcp")];
2140 assert_counter_eq(&map, OUTGOING_QUERIES_TOTAL, protocol, 1);
2141 }
2142
2143 #[cfg(feature = "__tls")]
2144 #[test]
2145 fn test_outgoing_query_protocol_metrics_tls() {
2146 subscribe();
2147 let recorder = DebuggingRecorder::new();
2148 let snapshotter = recorder.snapshotter();
2149
2150 with_local_recorder(&recorder, || {
2151 let runtime = tokio::runtime::Builder::new_current_thread()
2152 .enable_all()
2153 .build()
2154 .unwrap();
2155
2156 runtime.block_on(async {
2157 let options = ResolverOpts::default();
2158 let config = NameServerConfig::tls(
2159 IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)),
2160 "dns.google".into(),
2161 );
2162 let name_server = Arc::new(NameServer::new(
2163 [],
2164 config,
2165 &options,
2166 MockProvider::default(),
2167 ));
2168
2169 let cx = Arc::new(PoolContext::new(options, TlsConfig::new().unwrap()));
2170 let name = Name::parse("www.example.com.", None).unwrap();
2171 let _ = name_server
2172 .send(
2173 DnsRequest::from_query(
2174 Query::query(name.clone(), RecordType::A),
2175 DnsRequestOptions::default(),
2176 ),
2177 ConnectionPolicy::default(),
2178 &cx,
2179 )
2180 .await;
2181 });
2182 });
2183
2184 #[allow(clippy::mutable_key_type)]
2185 let map = snapshotter.snapshot().into_hashmap();
2186
2187 let protocol = vec![Label::new("protocol", "tls")];
2189 assert_counter_eq(&map, OUTGOING_QUERIES_TOTAL, protocol, 1);
2190 }
2191
2192 #[test]
2193 fn test_name_server_query_success_metrics() {
2194 subscribe();
2195 let recorder = DebuggingRecorder::new();
2196 let snapshotter = recorder.snapshotter();
2197
2198 with_local_recorder(&recorder, || {
2199 let runtime = tokio::runtime::Builder::new_current_thread()
2200 .enable_all()
2201 .build()
2202 .unwrap();
2203
2204 runtime.block_on(async {
2205 let options = ResolverOpts {
2206 enable_per_name_server_metrics: true,
2207 ..ResolverOpts::default()
2208 };
2209 let config = NameServerConfig::udp(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)));
2210 let mock_provider = MockProvider::default();
2211 let name_server = Arc::new(NameServer::new([], config, &options, mock_provider));
2212
2213 let cx = Arc::new(PoolContext::new(options, TlsConfig::new().unwrap()));
2214 let name = Name::parse("www.example.com.", None).unwrap();
2215 let _ = name_server
2216 .send(
2217 DnsRequest::from_query(
2218 Query::query(name.clone(), RecordType::A),
2219 DnsRequestOptions::default(),
2220 ),
2221 ConnectionPolicy::default(),
2222 &cx,
2223 )
2224 .await;
2225 });
2226 });
2227
2228 #[allow(clippy::mutable_key_type)]
2229 let map = snapshotter.snapshot().into_hashmap();
2230
2231 use crate::metrics::NAME_SERVER_QUERY_RESPONSES;
2232 let labels = vec![
2233 Label::new("addr", "8.8.8.8"),
2234 Label::new("protocol", "udp"),
2235 Label::new("status", "success"),
2236 ];
2237 assert_counter_eq(&map, NAME_SERVER_QUERY_RESPONSES, labels, 1);
2238 }
2239
2240 #[test]
2241 fn test_name_server_query_connection_failure_metrics() {
2242 subscribe();
2243 let recorder = DebuggingRecorder::new();
2244 let snapshotter = recorder.snapshotter();
2245
2246 with_local_recorder(&recorder, || {
2247 let runtime = tokio::runtime::Builder::new_current_thread()
2248 .enable_all()
2249 .build()
2250 .unwrap();
2251
2252 runtime.block_on(async {
2253 let options = ResolverOpts {
2254 enable_per_name_server_metrics: true,
2255 ..ResolverOpts::default()
2256 };
2257 let config = NameServerConfig::udp(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)));
2258 let mock_provider = MockProvider {
2259 new_connection_error: Some(NetError::Io(Arc::new(std::io::Error::new(
2260 std::io::ErrorKind::ConnectionRefused,
2261 "connection refused",
2262 )))),
2263 ..MockProvider::default()
2264 };
2265 let name_server = Arc::new(NameServer::new([], config, &options, mock_provider));
2266
2267 let cx = Arc::new(PoolContext::new(options, TlsConfig::new().unwrap()));
2268 let name = Name::parse("www.example.com.", None).unwrap();
2269 let _ = name_server
2270 .send(
2271 DnsRequest::from_query(
2272 Query::query(name.clone(), RecordType::A),
2273 DnsRequestOptions::default(),
2274 ),
2275 ConnectionPolicy::default(),
2276 &cx,
2277 )
2278 .await;
2279 });
2280 });
2281
2282 #[allow(clippy::mutable_key_type)]
2283 let map = snapshotter.snapshot().into_hashmap();
2284
2285 use crate::metrics::NAME_SERVER_QUERY_RESPONSES;
2286 let labels = vec![
2287 Label::new("addr", "8.8.8.8"),
2288 Label::new("protocol", "udp"),
2289 Label::new("status", "io_connection_refused"),
2290 ];
2291 assert_counter_eq(&map, NAME_SERVER_QUERY_RESPONSES, labels, 1);
2292 }
2293
2294 #[test]
2295 fn test_name_server_query_response_code_failure_metrics() {
2296 subscribe();
2297 let recorder = DebuggingRecorder::new();
2298 let snapshotter = recorder.snapshotter();
2299
2300 with_local_recorder(&recorder, || {
2301 let runtime = tokio::runtime::Builder::new_current_thread()
2302 .enable_all()
2303 .build()
2304 .unwrap();
2305
2306 runtime.block_on(async {
2307 let options = ResolverOpts {
2308 enable_per_name_server_metrics: true,
2309 ..ResolverOpts::default()
2310 };
2311 let config = NameServerConfig::udp(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)));
2312 let mock_provider = MockProvider::default();
2313 mock_provider
2314 .send_outcomes
2315 .lock()
2316 .push_back(Some(NetError::Dns(DnsError::ResponseCode(
2317 ResponseCode::ServFail,
2318 ))));
2319 let name_server = Arc::new(NameServer::new([], config, &options, mock_provider));
2320
2321 let cx = Arc::new(PoolContext::new(options, TlsConfig::new().unwrap()));
2322 let name = Name::parse("www.example.com.", None).unwrap();
2323 let _ = name_server
2324 .send(
2325 DnsRequest::from_query(
2326 Query::query(name.clone(), RecordType::A),
2327 DnsRequestOptions::default(),
2328 ),
2329 ConnectionPolicy::default(),
2330 &cx,
2331 )
2332 .await;
2333 });
2334 });
2335
2336 #[allow(clippy::mutable_key_type)]
2337 let map = snapshotter.snapshot().into_hashmap();
2338
2339 use crate::metrics::NAME_SERVER_QUERY_RESPONSES;
2340 let labels = vec![
2341 Label::new("addr", "8.8.8.8"),
2342 Label::new("protocol", "udp"),
2343 Label::new("status", "dns_response_code_servfail"),
2344 ];
2345 assert_counter_eq(&map, NAME_SERVER_QUERY_RESPONSES, labels, 1);
2346 }
2347
2348 #[test]
2349 fn test_name_server_query_no_records_success_metrics() {
2350 subscribe();
2351 let recorder = DebuggingRecorder::new();
2352 let snapshotter = recorder.snapshotter();
2353
2354 with_local_recorder(&recorder, || {
2355 let runtime = tokio::runtime::Builder::new_current_thread()
2356 .enable_all()
2357 .build()
2358 .unwrap();
2359
2360 runtime.block_on(async {
2361 let options = ResolverOpts {
2362 enable_per_name_server_metrics: true,
2363 ..ResolverOpts::default()
2364 };
2365 let config = NameServerConfig::udp(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)));
2366 let name = Name::parse("www.example.com.", None).unwrap();
2367 let query = Query::query(name.clone(), RecordType::A);
2368 let mock_provider = MockProvider::default();
2369 mock_provider
2370 .send_outcomes
2371 .lock()
2372 .push_back(Some(NetError::Dns(DnsError::NoRecordsFound(
2373 NoRecords::new(query, ResponseCode::NXDomain),
2374 ))));
2375 let name_server = Arc::new(NameServer::new([], config, &options, mock_provider));
2376
2377 let cx = Arc::new(PoolContext::new(options, TlsConfig::new().unwrap()));
2378 let _ = name_server
2379 .send(
2380 DnsRequest::from_query(
2381 Query::query(name.clone(), RecordType::A),
2382 DnsRequestOptions::default(),
2383 ),
2384 ConnectionPolicy::default(),
2385 &cx,
2386 )
2387 .await;
2388 });
2389 });
2390
2391 #[allow(clippy::mutable_key_type)]
2392 let map = snapshotter.snapshot().into_hashmap();
2393
2394 use crate::metrics::NAME_SERVER_QUERY_RESPONSES;
2395 let labels = vec![
2396 Label::new("addr", "8.8.8.8"),
2397 Label::new("protocol", "udp"),
2398 Label::new("status", "dns_no_records"),
2399 ];
2400 assert_counter_eq(&map, NAME_SERVER_QUERY_RESPONSES, labels, 1);
2401 }
2402
2403 #[test]
2404 fn test_name_server_query_metrics_aggregation() {
2405 subscribe();
2406 let recorder = DebuggingRecorder::new();
2407 let snapshotter = recorder.snapshotter();
2408
2409 with_local_recorder(&recorder, || {
2410 let runtime = tokio::runtime::Builder::new_current_thread()
2411 .enable_all()
2412 .build()
2413 .unwrap();
2414
2415 runtime.block_on(async {
2416 let name = Name::parse("www.example.com.", None).unwrap();
2417 let options = ResolverOpts {
2418 enable_per_name_server_metrics: false,
2419 ..Default::default()
2420 };
2421 let config = NameServerConfig::udp(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)));
2422 let mock_provider = MockProvider::default();
2423 let name_server = Arc::new(NameServer::new([], config, &options, mock_provider));
2424
2425 let cx = Arc::new(PoolContext::new(options, TlsConfig::new().unwrap()));
2426 let _ = name_server
2427 .send(
2428 DnsRequest::from_query(
2429 Query::query(name.clone(), RecordType::A),
2430 DnsRequestOptions::default(),
2431 ),
2432 ConnectionPolicy::default(),
2433 &cx,
2434 )
2435 .await;
2436 });
2437 });
2438
2439 #[allow(clippy::mutable_key_type)]
2440 let map = snapshotter.snapshot().into_hashmap();
2441
2442 use crate::metrics::NAME_SERVER_QUERY_RESPONSES;
2443 let labels = vec![
2444 Label::new("protocol", "udp"),
2446 Label::new("status", "success"),
2447 ];
2448 assert_counter_eq(&map, NAME_SERVER_QUERY_RESPONSES, labels, 1);
2449 }
2450}
2451
2452#[cfg(all(test, any(feature = "metrics", feature = "__tls")))]
2453mod reconnect_tests {
2454 use std::collections::VecDeque;
2455 use std::io;
2456 use std::net::{IpAddr, Ipv4Addr};
2457 use std::sync::Arc;
2458
2459 use parking_lot::Mutex as SyncMutex;
2460 use test_support::subscribe;
2461
2462 use super::mock_provider::MockProvider;
2463 use super::{ConnectionPolicy, NameServer};
2464 use crate::config::{NameServerConfig, ResolverOpts};
2465 use crate::connection_provider::TlsConfig;
2466 use crate::name_server_pool::PoolContext;
2467 use crate::net::NetError;
2468 use crate::proto::op::{DnsRequest, DnsRequestOptions, Query};
2469 use crate::proto::rr::{Name, RecordType};
2470
2471 fn connection_closed() -> NetError {
2472 NetError::from(io::Error::from(io::ErrorKind::ConnectionReset))
2473 }
2474
2475 fn query() -> DnsRequest {
2476 DnsRequest::from_query(
2477 Query::query(
2478 Name::parse("www.example.com.", None).unwrap(),
2479 RecordType::A,
2480 ),
2481 DnsRequestOptions::default(),
2482 )
2483 }
2484
2485 fn name_server_with(
2487 outcomes: VecDeque<Option<NetError>>,
2488 ) -> (
2489 Arc<NameServer<MockProvider>>,
2490 MockProvider,
2491 Arc<PoolContext>,
2492 ) {
2493 let provider = MockProvider {
2494 send_outcomes: Arc::new(SyncMutex::new(outcomes)),
2495 ..MockProvider::default()
2496 };
2497 let options = ResolverOpts::default();
2498 let config = NameServerConfig::udp(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)));
2499 let ns = Arc::new(NameServer::new([], config, &options, provider.clone()));
2500 let cx = Arc::new(PoolContext::new(options, TlsConfig::new().unwrap()));
2501 (ns, provider, cx)
2502 }
2503
2504 #[tokio::test]
2507 async fn reconnects_once_when_reused_connection_was_closed() {
2508 subscribe();
2509
2510 let (ns, provider, cx) =
2513 name_server_with(VecDeque::from([None, Some(connection_closed())]));
2514
2515 ns.clone()
2516 .send(query(), ConnectionPolicy::default(), &cx)
2517 .await
2518 .expect("initial query establishes the connection");
2519 ns.clone()
2520 .send(query(), ConnectionPolicy::default(), &cx)
2521 .await
2522 .expect("stale reused connection is transparently reconnected");
2523
2524 assert_eq!(provider.new_connection_calls().len(), 2);
2526 }
2527
2528 #[tokio::test]
2531 async fn does_not_retry_when_a_fresh_connection_fails_closed() {
2532 subscribe();
2533
2534 let (ns, provider, cx) = name_server_with(VecDeque::from([Some(connection_closed())]));
2535
2536 ns.send(query(), ConnectionPolicy::default(), &cx)
2537 .await
2538 .expect_err("a fresh connection failure must not be retried");
2539
2540 assert_eq!(provider.new_connection_calls().len(), 1);
2541 }
2542}
2543
2544#[cfg(all(test, any(feature = "metrics", feature = "__tls")))]
2545mod mock_provider {
2546 use std::collections::VecDeque;
2547 use std::future::Future;
2548 use std::io;
2549 use std::pin::Pin;
2550 use std::task::{Context, Poll};
2551
2552 use futures_util::stream::once;
2553 use futures_util::{Stream, future};
2554 use tokio::net::UdpSocket;
2555
2556 use super::*;
2557 use crate::config::ProtocolConfig;
2558 use crate::net::runtime::TokioTime;
2559 use crate::net::runtime::iocompat::AsyncIoTokioAsStd;
2560 use crate::proto::op::Message;
2561 use crate::proto::rr::rdata::NULL;
2562 use crate::proto::rr::{RData, Record};
2563
2564 #[derive(Clone)]
2571 pub(super) struct MockProvider {
2572 pub(super) runtime: MockSyncRuntimeProvider,
2573 pub(super) new_connection_calls: Arc<SyncMutex<Vec<(IpAddr, ProtocolConfig)>>>,
2574 pub(super) new_connection_error: Option<NetError>,
2575 pub(super) send_outcomes: Arc<SyncMutex<VecDeque<Option<NetError>>>>,
2579 }
2580
2581 impl MockProvider {
2582 pub(super) fn new_connection_calls(&self) -> Vec<(IpAddr, ProtocolConfig)> {
2583 self.new_connection_calls.lock().clone()
2584 }
2585 }
2586
2587 impl ConnectionProvider for MockProvider {
2588 type Conn = MockClientHandle;
2589 type FutureConn = Pin<Box<dyn Send + Future<Output = Result<Self::Conn, NetError>>>>;
2590 type RuntimeProvider = MockSyncRuntimeProvider;
2591
2592 fn new_connection(
2593 &self,
2594 ip: IpAddr,
2595 config: &ConnectionConfig,
2596 _cx: &PoolContext,
2597 ) -> Result<Self::FutureConn, NetError> {
2598 self.new_connection_calls
2599 .lock()
2600 .push((ip, config.protocol.clone()));
2601
2602 Ok(Box::pin(future::ready(match &self.new_connection_error {
2603 Some(err) => Err(err.clone()),
2604 None => Ok(MockClientHandle {
2605 send_outcomes: self.send_outcomes.clone(),
2606 }),
2607 })))
2608 }
2609
2610 fn runtime_provider(&self) -> &Self::RuntimeProvider {
2611 &self.runtime
2612 }
2613 }
2614
2615 impl Default for MockProvider {
2616 fn default() -> Self {
2617 Self {
2618 runtime: MockSyncRuntimeProvider,
2619 new_connection_calls: Arc::new(SyncMutex::new(Vec::new())),
2620 new_connection_error: None,
2621 send_outcomes: Arc::new(SyncMutex::new(VecDeque::new())),
2622 }
2623 }
2624 }
2625
2626 #[derive(Clone, Default)]
2631 pub(super) struct MockClientHandle {
2632 send_outcomes: Arc<SyncMutex<VecDeque<Option<NetError>>>>,
2633 }
2634
2635 impl DnsHandle for MockClientHandle {
2636 type Response = Pin<Box<dyn Stream<Item = Result<DnsResponse, NetError>> + Send>>;
2637 type Runtime = MockSyncRuntimeProvider;
2638
2639 fn send(&self, request: DnsRequest) -> Self::Response {
2640 if let Some(Some(err)) = self.send_outcomes.lock().pop_front() {
2641 return Box::pin(once(future::ready(Err(err))));
2642 }
2643 let mut response = Message::response(request.id, request.op_code);
2644 response.metadata.response_code = ResponseCode::NoError;
2645 response.add_queries(request.queries.clone());
2646 if let Some(query) = request.queries.first() {
2647 response.add_answer(Record::from_rdata(
2648 query.name.clone(),
2649 0,
2650 RData::NULL(NULL::with(vec![0])),
2651 ));
2652 }
2653 Box::pin(once(future::ready(Ok(
2654 DnsResponse::from_message(response).unwrap()
2655 ))))
2656 }
2657 }
2658
2659 #[derive(Clone)]
2663 pub(super) struct MockSyncRuntimeProvider;
2664
2665 impl RuntimeProvider for MockSyncRuntimeProvider {
2666 type Handle = MockSyncHandle;
2667 type Timer = TokioTime;
2668 type Udp = UdpSocket;
2669 type Tcp = AsyncIoTokioAsStd<tokio::net::TcpStream>;
2670
2671 fn create_handle(&self) -> Self::Handle {
2672 MockSyncHandle
2673 }
2674
2675 #[allow(clippy::unimplemented)]
2676 fn connect_tcp(
2677 &self,
2678 _server_addr: std::net::SocketAddr,
2679 _bind_addr: Option<std::net::SocketAddr>,
2680 _timeout: Option<Duration>,
2681 ) -> Pin<Box<dyn Future<Output = Result<Self::Tcp, io::Error>> + Send>> {
2682 unimplemented!();
2683 }
2684
2685 #[allow(clippy::unimplemented)]
2686 fn bind_udp(
2687 &self,
2688 _local_addr: std::net::SocketAddr,
2689 _server_addr: std::net::SocketAddr,
2690 ) -> Pin<Box<dyn Future<Output = Result<Self::Udp, io::Error>> + Send>> {
2691 unimplemented!();
2692 }
2693 }
2694
2695 #[derive(Clone)]
2700 pub(super) struct MockSyncHandle;
2701
2702 impl Spawn for MockSyncHandle {
2703 fn spawn_bg(&mut self, future: impl Future<Output = ()> + Send + 'static) {
2704 let waker = futures_util::task::noop_waker();
2707 let mut context = Context::from_waker(&waker);
2708 let mut future = Box::pin(future);
2709
2710 loop {
2711 match future.as_mut().poll(&mut context) {
2712 Poll::Ready(_) => break,
2713 Poll::Pending => continue,
2714 }
2715 }
2716 }
2717 }
2718}