1use std::collections::{HashMap, VecDeque};
9use std::net::IpAddr;
10use std::pin::Pin;
11use std::sync::atomic::AtomicU8;
12use std::sync::{
13 Arc,
14 atomic::{AtomicUsize, Ordering as AtomicOrdering},
15};
16use std::task::{Context, Poll};
17use std::time::{Duration, Instant, SystemTime};
18
19use futures_util::lock::{Mutex as AsyncMutex, MutexGuard};
20use futures_util::stream::{FuturesUnordered, Stream, StreamExt, once};
21use futures_util::{
22 Future, FutureExt,
23 future::{BoxFuture, Shared},
24};
25use parking_lot::Mutex;
26#[cfg(feature = "serde")]
27use serde::{Deserialize, Serialize};
28use smallvec::SmallVec;
29#[cfg(all(feature = "toml", any(feature = "__tls", feature = "__quic")))]
30use tracing::info;
31use tracing::{debug, error};
32
33#[cfg(any(feature = "__tls", feature = "__quic"))]
34use crate::config::OpportunisticEncryptionConfig;
35use crate::{
36 config::{NameServerConfig, OpportunisticEncryption, ResolverOpts, ServerOrderingStrategy},
37 connection_provider::{ConnectionProvider, TlsConfig},
38 name_server::{ConnectionPolicy, NameServer},
39 net::{
40 DnsError, NetError, NoRecords,
41 runtime::{RuntimeProvider, Time},
42 xfer::{DnsHandle, Protocol},
43 },
44 proto::{
45 access_control::AccessControlSet,
46 op::{DnsRequest, DnsRequestOptions, DnsResponse, OpCode, Query, ResponseCode},
47 rr::{
48 Name, RData, Record,
49 rdata::{
50 A, AAAA,
51 opt::{ClientSubnet, EdnsCode, EdnsOption},
52 },
53 },
54 },
55};
56
57#[derive(Clone)]
59pub struct NameServerPool<P: ConnectionProvider> {
60 state: Arc<PoolState<P>>,
61 active_requests: Arc<Mutex<HashMap<Arc<CacheKey>, SharedLookup>>>,
62 ttl: Option<TtlInstant>,
63 zone: Option<Name>,
64}
65
66impl<P: ConnectionProvider> NameServerPool<P> {
67 pub fn from_config(
69 servers: impl IntoIterator<Item = NameServerConfig>,
70 cx: Arc<PoolContext>,
71 conn_provider: P,
72 ) -> Self {
73 Self::from_nameservers(
74 servers
75 .into_iter()
76 .map(|server| {
77 Arc::new(NameServer::new(
78 [],
79 server,
80 &cx.options,
81 conn_provider.clone(),
82 ))
83 })
84 .collect(),
85 cx,
86 )
87 }
88
89 #[doc(hidden)]
90 pub fn from_nameservers(servers: Vec<Arc<NameServer<P>>>, cx: Arc<PoolContext>) -> Self {
91 Self {
92 state: Arc::new(PoolState {
93 servers,
94 cx,
95 next: AtomicUsize::new(0),
96 }),
97 active_requests: Arc::new(Mutex::new(HashMap::new())),
98 ttl: None,
99 zone: None,
100 }
101 }
102
103 pub fn with_ttl(mut self, ttl: Duration) -> Self {
105 self.ttl = Some(TtlInstant::now() + ttl);
106 self
107 }
108
109 pub fn with_zone(mut self, zone: Name) -> Self {
111 self.zone = Some(zone);
112 self
113 }
114
115 pub fn ttl_expired(&self) -> bool {
117 match self.ttl {
118 Some(ttl) => TtlInstant::now() > ttl,
119 None => false,
120 }
121 }
122
123 pub fn context(&self) -> &Arc<PoolContext> {
125 &self.state.cx
126 }
127
128 pub fn zone(&self) -> Option<&Name> {
130 self.zone.as_ref()
131 }
132}
133
134#[cfg(not(feature = "tokio"))]
136type TtlInstant = std::time::Instant;
137#[cfg(feature = "tokio")]
138type TtlInstant = tokio::time::Instant;
139
140impl<P: ConnectionProvider> DnsHandle for NameServerPool<P> {
141 type Response = Pin<Box<dyn Stream<Item = Result<DnsResponse, NetError>> + Send>>;
142 type Runtime = P::RuntimeProvider;
143
144 fn lookup(&self, query: Query, mut options: DnsRequestOptions) -> Self::Response {
145 debug!("querying: {} {:?}", query.name(), query.query_type());
146 options.case_randomization = self.state.cx.options.case_randomization;
147 self.send(DnsRequest::from_query(query, options))
148 }
149
150 fn send(&self, request: DnsRequest) -> Self::Response {
151 let state = self.state.clone();
152 let acs = self.state.cx.answer_address_filter.clone();
153 let active_requests = self.active_requests.clone();
154
155 Box::pin(once(async move {
156 debug!("sending request: {:?}", request.queries);
157 let query = match request.queries.first() {
158 Some(q) => q.clone(),
159 None => return Err("no query in request".into()),
160 };
161
162 let key = Arc::new(CacheKey::from_request(&request));
163
164 let (lookup, is_creator) = {
165 let mut active = active_requests.lock();
166 if let Some(existing) = active.get(&key) {
167 debug!(%query, "query currently in progress - returning shared lookup");
168 (existing.clone(), false)
169 } else {
170 debug!(%query, "creating new shared lookup");
171
172 let lookup = async move {
173 match state.try_send(request).await {
174 Ok(response) => Some(Ok(response)),
175 Err(e) => Some(Err(e)),
176 }
177 }
178 .boxed()
179 .shared();
180
181 let shared_lookup = SharedLookup(lookup);
182 active.insert(key.clone(), shared_lookup.clone());
183 (shared_lookup, true)
184 }
185 };
186
187 let _cleanup = is_creator.then(|| ActiveRequestCleanup {
193 active_requests: active_requests.clone(),
194 key: key.clone(),
195 });
196
197 let response = lookup.await;
198 let mut response = response?;
199
200 for record in response
201 .answers
202 .iter()
203 .chain(response.authorities.iter())
204 .chain(response.additionals.iter())
205 {
206 if record.dns_class == query.query_class {
207 continue;
208 }
209 error!(
210 %query,
211 record_name = %record.name,
212 record_class = %record.dns_class,
213 record_type = %record.record_type(),
214 "rejecting response: record class does not match query class",
215 );
216 return Err(NetError::ForeignClassRecord {
217 record_name: record.name.clone(),
218 record_class: record.dns_class,
219 record_type: record.record_type(),
220 });
221 }
222
223 if acs.allows_all() {
224 return Ok(response);
225 }
226
227 let answer_filter = |record: &Record| {
228 let ip = match &record.data {
229 RData::A(A(ipv4)) => (*ipv4).into(),
230 RData::AAAA(AAAA(ipv6)) => (*ipv6).into(),
231 _ => return true,
232 };
233
234 if acs.denied(ip) {
235 error!(
236 %query,
237 %ip,
238 "removing ip from response: answer filter matched"
239 );
240
241 false
242 } else {
243 true
244 }
245 };
246
247 let answers_len = response.answers.len();
248 let authorities_len = response.authorities.len();
249
250 response.additionals.retain(answer_filter);
251 response.answers.retain(answer_filter);
252 response.authorities.retain(answer_filter);
253
254 if response.answers.is_empty() && answers_len != 0
255 || (response.answers.is_empty()
256 && response.authorities.is_empty()
257 && authorities_len != 0)
258 {
259 return Err(NoRecords::new(Box::new(query.clone()), ResponseCode::NXDomain).into());
260 }
261
262 DnsResponse::from_message(response.into_message()).map_err(NetError::from)
265 }))
266 }
267}
268
269struct PoolState<P: ConnectionProvider> {
270 servers: Vec<Arc<NameServer<P>>>,
271 cx: Arc<PoolContext>,
272 next: AtomicUsize,
273}
274
275impl<P: ConnectionProvider> PoolState<P> {
276 async fn try_send(&self, request: DnsRequest) -> Result<DnsResponse, NetError> {
277 let mut servers = self.servers.clone();
278 match self.cx.options.server_ordering_strategy {
279 ServerOrderingStrategy::QueryStatistics => {
283 sort_servers_by_query_statistics(&mut servers);
284 }
285 ServerOrderingStrategy::UserProvidedOrder => {}
286 ServerOrderingStrategy::RoundRobin => {
287 let num_concurrent_reqs = if self.cx.options.num_concurrent_reqs > 1 {
288 self.cx.options.num_concurrent_reqs
289 } else {
290 1
291 };
292 if num_concurrent_reqs < servers.len() {
293 let index = self
294 .next
295 .fetch_add(num_concurrent_reqs, AtomicOrdering::SeqCst)
296 % servers.len();
297 servers.rotate_left(index);
298 }
299 }
300 }
301
302 let deadline = Instant::now() + self.cx.options.timeout;
314
315 let mut servers = VecDeque::from(servers);
316 let mut backoff = Duration::from_millis(20);
317 let mut busy = SmallVec::<[Arc<NameServer<P>>; 2]>::new();
318 let mut err = NetError::NoConnections;
319 let mut policy = ConnectionPolicy::default();
320
321 loop {
322 if Instant::now() >= deadline {
324 return Err(NetError::Timeout);
325 }
326
327 let mut par_servers = SmallVec::<[_; 2]>::new();
329 while !servers.is_empty()
330 && par_servers.len() < Ord::max(self.cx.options.num_concurrent_reqs, 1)
331 {
332 if let Some(server) = servers.pop_front() {
333 if policy.allows_server(&server) {
334 par_servers.push(server);
335 }
336 }
337 }
338
339 if par_servers.is_empty() {
340 if !busy.is_empty() && backoff < Duration::from_millis(300) {
341 let remaining = deadline.saturating_duration_since(Instant::now());
343 if remaining.is_zero() {
344 return Err(NetError::Timeout);
345 }
346 <<P as ConnectionProvider>::RuntimeProvider as RuntimeProvider>::Timer::delay_for(
347 backoff.min(remaining),
348 ).await;
349 servers.extend(busy.drain(..).filter(|ns| policy.allows_server(ns)));
350 backoff *= 2;
351 continue;
352 }
353 return Err(err);
354 }
355
356 let in_flight = par_servers.iter().cloned().collect::<SmallVec<[_; 2]>>();
359
360 let batch_start = Instant::now();
361 let mut requests = par_servers
362 .into_iter()
363 .map(|server| {
364 let mut request = request.clone();
365
366 let retry_interval =
368 Duration::from_micros((server.decayed_srtt() * 1.2) as u64);
369 request.options_mut().retry_interval = retry_interval;
370 debug!(?retry_interval, ip = ?server.ip(), "setting retry_interval");
371
372 let future = server.clone().send(request, policy, &self.cx);
373 async { (server, future.await) }
374 })
375 .collect::<FuturesUnordered<_>>();
376
377 let mut completed = SmallVec::<[IpAddr; 2]>::new();
380
381 while let Some((server, result)) = requests.next().await {
382 completed.push(server.ip());
383 let e = match result {
384 Ok(response) if response.truncation && policy.disable_udp => {
385 debug!("truncated response received and UDP already disabled, giving up");
386 NetError::Truncated
387 }
388 Ok(response) if response.truncation => {
389 debug!("truncated response received, retrying over TCP");
390 policy.disable_udp = true;
391 err = NetError::Truncated;
392 servers.push_front(server);
393 continue;
394 }
395 Ok(response) => {
396 let winner_rtt = batch_start.elapsed();
398 for abandoned in &in_flight {
399 if !completed.contains(&abandoned.ip()) {
400 debug!(ip = ?abandoned.ip(), ?winner_rtt, "recording cancelled parallel server");
401 abandoned.record_cancelled(winner_rtt);
402 }
403 }
404 return Ok(response);
405 }
406 Err(e) => e,
407 };
408
409 match &e {
410 NetError::QueryCaseMismatch => {
413 servers.push_front(server);
414 policy.disable_udp = true;
415 continue;
416 }
417 NetError::Busy => busy.push(server),
419 NetError::Io(_) | NetError::NoConnections | NetError::Timeout => {}
421 NetError::Dns(DnsError::NoRecordsFound(NoRecords {
424 response_code: ResponseCode::NXDomain,
425 ..
426 })) if !server.trust_negative_responses() => {}
427 _ => return Err(e),
428 }
429
430 err = most_specific(err, e);
431 }
432 }
433 }
434}
435
436fn most_specific(previous: NetError, current: NetError) -> NetError {
438 match (&previous, ¤t) {
439 (
440 NetError::Dns(DnsError::NoRecordsFound { .. }),
441 NetError::Dns(DnsError::NoRecordsFound { .. }),
442 ) => return previous,
443 (NetError::Dns(DnsError::NoRecordsFound { .. }), _) => return previous,
444 (_, NetError::Dns(DnsError::NoRecordsFound { .. })) => return current,
445 _ => (),
446 }
447
448 match (&previous, ¤t) {
449 (NetError::Io { .. }, NetError::Io { .. }) => return previous,
450 (NetError::Io { .. }, _) => return current,
451 (_, NetError::Io { .. }) => return previous,
452 _ => (),
453 }
454
455 match (&previous, ¤t) {
456 (NetError::Timeout, NetError::Timeout) => return previous,
457 (NetError::Timeout, _) => return previous,
458 (_, NetError::Timeout) => return current,
459 _ => (),
460 }
461
462 previous
463}
464
465pub(crate) fn sort_servers_by_query_statistics<P: ConnectionProvider>(
472 servers: &mut [Arc<NameServer<P>>],
473) {
474 servers.sort_by_cached_key(|s| s.decayed_srtt().to_bits());
477}
478
479#[non_exhaustive]
481pub struct PoolContext {
482 pub options: ResolverOpts,
484 #[cfg(feature = "__tls")]
486 pub tls: rustls::ClientConfig,
487 pub opportunistic_probe_budget: AtomicU8,
489 pub opportunistic_encryption: OpportunisticEncryption,
491 pub transport_state: AsyncMutex<NameServerTransportState>,
493 pub answer_address_filter: AccessControlSet,
495}
496
497impl PoolContext {
498 #[cfg_attr(not(feature = "__tls"), expect(unused_variables))]
500 pub fn new(options: ResolverOpts, tls: TlsConfig) -> Self {
501 Self {
502 answer_address_filter: options.answer_address_filter(),
503 options,
504 #[cfg(feature = "__tls")]
505 tls: tls.config,
506 opportunistic_probe_budget: AtomicU8::default(),
507 opportunistic_encryption: OpportunisticEncryption::default(),
508 transport_state: AsyncMutex::new(NameServerTransportState::default()),
509 }
510 }
511
512 pub fn with_probe_budget(self, budget: u8) -> Self {
514 self.opportunistic_probe_budget
515 .store(budget, AtomicOrdering::SeqCst);
516 self
517 }
518
519 pub fn with_answer_filter(mut self, answer_filter: AccessControlSet) -> Self {
521 self.answer_address_filter = answer_filter;
522 self
523 }
524
525 #[cfg(any(feature = "__tls", feature = "__quic"))]
527 pub fn with_opportunistic_encryption(mut self) -> Self {
528 self.opportunistic_encryption = OpportunisticEncryption::Enabled {
529 config: OpportunisticEncryptionConfig::default(),
530 };
531 self
532 }
533
534 pub fn with_transport_state(mut self, transport_state: NameServerTransportState) -> Self {
536 self.transport_state = AsyncMutex::new(transport_state);
537 self
538 }
539
540 pub(crate) async fn transport_state(&self) -> MutexGuard<'_, NameServerTransportState> {
541 self.transport_state.lock().await
542 }
543}
544
545#[derive(Debug, Default, Clone)]
547#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
548#[repr(transparent)]
549pub struct NameServerTransportState(HashMap<IpAddr, ProtocolTransportState>);
550
551impl NameServerTransportState {
552 pub fn nameserver_count(&self) -> usize {
554 self.0.len()
555 }
556
557 pub(crate) fn initiate_connection(&mut self, ip: IpAddr, protocol: Protocol) {
559 let protocol_state = self.0.entry(ip).or_default();
560 *protocol_state.get_mut(protocol) = TransportState::default();
561 }
562
563 pub(crate) fn complete_connection(&mut self, ip: IpAddr, protocol: Protocol) {
565 let protocol_state = self.0.entry(ip).or_default();
566 *protocol_state.get_mut(protocol) = TransportState::Success {
567 last_response: None,
568 };
569 }
570
571 pub(crate) fn response_received(&mut self, ip: IpAddr, protocol: Protocol) {
573 let Some(protocol_state) = self.0.get_mut(&ip) else {
574 return;
575 };
576 let TransportState::Success { last_response, .. } = protocol_state.get_mut(protocol) else {
577 return;
578 };
579 *last_response = Some(SystemTime::now());
580 }
581
582 pub(crate) fn error_received(&mut self, ip: IpAddr, protocol: Protocol, error: &NetError) {
584 let protocol_state = self.0.entry(ip).or_default();
585 *protocol_state.get_mut(protocol) = match &error {
586 NetError::Timeout => TransportState::TimedOut {
587 #[cfg(any(feature = "__tls", feature = "__quic"))]
588 completed_at: SystemTime::now(),
589 },
590 _ => TransportState::Failed {
591 #[cfg(any(feature = "__tls", feature = "__quic"))]
592 completed_at: SystemTime::now(),
593 },
594 };
595 }
596
597 #[cfg(any(feature = "__tls", feature = "__quic"))]
600 pub(crate) fn any_recent_success(&self, ip: IpAddr, config: &OpportunisticEncryption) -> bool {
601 #[allow(unused_assignments, unused_mut)]
602 let mut tls_success = false;
603 #[allow(unused_assignments, unused_mut)]
604 let mut quic_success = false;
605
606 #[cfg(feature = "__tls")]
607 {
608 tls_success = self.recent_success(ip, Protocol::Tls, config);
609 }
610
611 #[cfg(feature = "__quic")]
612 {
613 quic_success = self.recent_success(ip, Protocol::Quic, config);
614 }
615
616 tls_success || quic_success
617 }
618
619 #[cfg(not(any(feature = "__tls", feature = "__quic")))]
621 pub(crate) fn any_recent_success(
622 &self,
623 _ip: IpAddr,
624 _config: &OpportunisticEncryption,
625 ) -> bool {
626 false
627 }
628
629 #[cfg(any(feature = "__tls", feature = "__quic"))]
635 pub(crate) fn recent_success(
636 &self,
637 ip: IpAddr,
638 protocol: Protocol,
639 config: &OpportunisticEncryption,
640 ) -> bool {
641 let OpportunisticEncryption::Enabled { config } = config else {
642 return false;
643 };
644
645 let Some(protocol_state) = self.0.get(&ip) else {
646 return false;
647 };
648
649 let TransportState::Success { last_response, .. } = protocol_state.get(protocol) else {
650 return false;
651 };
652
653 let Some(last_response) = last_response else {
654 return false;
655 };
656
657 last_response.elapsed().unwrap_or(Duration::MAX) <= config.persistence_period
658 }
659
660 #[cfg(not(any(feature = "__tls", feature = "__quic")))]
665 pub(crate) fn recent_success(
666 &self,
667 _ip: IpAddr,
668 _protocol: Protocol,
669 _config: &OpportunisticEncryption,
670 ) -> bool {
671 false
672 }
673
674 #[cfg(any(feature = "__tls", feature = "__quic"))]
676 pub(crate) fn should_probe_encrypted(
677 &self,
678 ip: IpAddr,
679 protocol: Protocol,
680 config: &OpportunisticEncryption,
681 ) -> bool {
682 debug_assert!(protocol.is_encrypted());
683
684 let OpportunisticEncryption::Enabled { config, .. } = config else {
685 return false;
686 };
687
688 let Some(protocol_state) = self.0.get(&ip) else {
689 return true;
690 };
691
692 match protocol_state.get(protocol) {
693 TransportState::Initiated => false,
694 TransportState::Success { .. } => true,
695 TransportState::Failed { completed_at } | TransportState::TimedOut { completed_at } => {
696 completed_at.elapsed().unwrap_or(Duration::MAX) > config.damping_period
697 }
698 }
699 }
700
701 #[cfg(not(any(feature = "__tls", feature = "__quic")))]
703 pub(crate) fn should_probe_encrypted(
704 &self,
705 _ip: IpAddr,
706 _protocol: Protocol,
707 _config: &OpportunisticEncryption,
708 ) -> bool {
709 false
710 }
711
712 #[cfg(all(test, feature = "__tls"))]
714 pub(crate) fn set_last_response(&mut self, ip: IpAddr, protocol: Protocol, when: SystemTime) {
715 let Some(protocol_state) = self.0.get_mut(&ip) else {
716 return;
717 };
718
719 let TransportState::Success { last_response, .. } = protocol_state.get_mut(protocol) else {
720 return;
721 };
722
723 *last_response = Some(when);
724 }
725
726 #[cfg(all(test, feature = "__tls"))]
728 pub(crate) fn set_failure_time(&mut self, ip: IpAddr, protocol: Protocol, when: SystemTime) {
729 let protocol_state = self.0.entry(ip).or_default();
730 *protocol_state.get_mut(protocol) = TransportState::Failed { completed_at: when };
731 }
732}
733
734#[derive(Debug, Clone, Copy, Default)]
735#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
736struct ProtocolTransportState {
737 #[cfg(feature = "__tls")]
738 tls: TransportState,
739 #[cfg(feature = "__quic")]
740 quic: TransportState,
741}
742
743impl ProtocolTransportState {
744 #[cfg_attr(not(any(feature = "__tls", feature = "__quic")), allow(dead_code))]
745 fn get_mut(&mut self, protocol: Protocol) -> &mut TransportState {
746 match protocol {
747 #[cfg(feature = "__tls")]
748 Protocol::Tls => &mut self.tls,
749 #[cfg(feature = "__quic")]
750 Protocol::Quic => &mut self.quic,
751 _ => unreachable!("unsupported opportunistic encryption protocol: {protocol:?}"),
752 }
753 }
754
755 #[cfg_attr(not(any(feature = "__tls", feature = "__quic")), allow(dead_code))]
756 fn get(&self, protocol: Protocol) -> &TransportState {
757 match protocol {
758 #[cfg(feature = "__tls")]
759 Protocol::Tls => &self.tls,
760 #[cfg(feature = "__quic")]
761 Protocol::Quic => &self.quic,
762 _ => unreachable!("unsupported opportunistic encryption protocol: {protocol:?}"),
763 }
764 }
765}
766
767#[derive(Debug, Clone, Copy, Default)]
769#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
770enum TransportState {
771 #[default]
773 Initiated,
774 Success {
776 last_response: Option<SystemTime>,
778 },
779 Failed {
781 #[cfg(any(feature = "__tls", feature = "__quic"))]
783 completed_at: SystemTime,
784 },
785 TimedOut {
787 #[cfg(any(feature = "__tls", feature = "__quic"))]
789 completed_at: SystemTime,
790 },
791}
792
793#[cfg(all(feature = "toml", any(feature = "__tls", feature = "__quic")))]
794pub use opportunistic_encryption_persistence::OpportunisticEncryptionStatePersistTask;
795
796#[cfg(all(feature = "toml", any(feature = "__tls", feature = "__quic")))]
797mod opportunistic_encryption_persistence {
798 #[cfg(unix)]
799 use std::fs::File;
800 use std::{
801 fs::{self, OpenOptions},
802 io::{self, Write},
803 marker::PhantomData,
804 path::{Path, PathBuf},
805 };
806
807 use tracing::trace;
808
809 use super::*;
810 use crate::config::OpportunisticEncryptionPersistence;
811 use crate::net::runtime::Spawn;
812
813 pub struct OpportunisticEncryptionStatePersistTask<T> {
815 cx: Arc<PoolContext>,
816 path: PathBuf,
817 save_interval: Duration,
818 _time: PhantomData<T>,
819 }
820
821 impl<T: Time> OpportunisticEncryptionStatePersistTask<T> {
822 pub async fn start<P: RuntimeProvider>(
824 config: OpportunisticEncryptionPersistence,
825 pool_context: &Arc<PoolContext>,
826 conn_provider: P,
827 ) -> Result<Option<P::Handle>, String> {
828 info!(
829 path = %config.path.display(),
830 save_interval = ?config.save_interval,
831 "spawning encrypted transport state persistence task"
832 );
833
834 let new =
835 OpportunisticEncryptionStatePersistTask::<P::Timer>::new(config, pool_context);
836
837 new.save(&*new.cx.transport_state.lock().await)
840 .map_err(|err| {
841 format!(
842 "failed to save opportunistic encryption state: {path}: {err}",
843 path = new.path.display()
844 )
845 })?;
846
847 let mut handle = conn_provider.create_handle();
848 handle.spawn_bg(new.run());
849 Ok(Some(handle))
850 }
851
852 fn new(config: OpportunisticEncryptionPersistence, cx: &Arc<PoolContext>) -> Self {
853 Self {
854 cx: cx.clone(),
855 path: config.path,
856 save_interval: config.save_interval,
857 _time: PhantomData,
858 }
859 }
860
861 async fn run(self) {
862 let Self {
863 save_interval,
864 path,
865 cx,
866 ..
867 } = &self;
868
869 loop {
870 T::delay_for(*save_interval).await;
871 trace!(path = %path.display(), ?save_interval, "persisting opportunistic encryption state");
872 if let Err(e) = self.save(&*cx.transport_state.lock().await) {
873 error!("failed to save opportunistic encryption state: {e}");
874 }
875 }
876 }
877
878 fn save(&self, state: &NameServerTransportState) -> Result<(), io::Error> {
879 let toml_content = toml::to_string_pretty(state).map_err(|e| {
880 io::Error::new(
881 io::ErrorKind::InvalidData,
882 format!("failed to serialize state to TOML: {e}"),
883 )
884 })?;
885
886 if let Some(parent) = parent_directory(&self.path) {
887 fs::create_dir_all(parent)?;
888 }
889
890 let temp_path = {
891 let mut temp = self.path.as_os_str().to_os_string();
892 temp.push(".tmp");
893 PathBuf::from(temp)
894 };
895
896 {
897 let mut temp_file = OpenOptions::new()
898 .write(true)
899 .create(true)
900 .truncate(true)
901 .open(&temp_path)?;
902
903 temp_file.write_all(toml_content.as_bytes())?;
904 temp_file.sync_all()?;
905 }
906
907 #[cfg(unix)]
908 if let Some(parent) = parent_directory(&self.path) {
909 File::open(parent)?.sync_all()?;
910 }
911
912 fs::rename(&temp_path, &self.path)?;
913 debug!(state_file = %self.path.display(), "saved opportunistic encryption state");
914 Ok(())
915 }
916 }
917
918 fn parent_directory(path: &Path) -> Option<&Path> {
920 let parent = path.parent()?;
921 Some(match parent == Path::new("") {
924 true => Path::new("."),
925 false => parent,
926 })
927 }
928}
929
930struct ActiveRequestCleanup {
937 active_requests: Arc<Mutex<HashMap<Arc<CacheKey>, SharedLookup>>>,
938 key: Arc<CacheKey>,
939}
940
941impl Drop for ActiveRequestCleanup {
942 fn drop(&mut self) {
943 self.active_requests.lock().remove(&self.key);
944 }
945}
946
947#[derive(PartialEq, Eq, Hash)]
949struct CacheKey {
950 op_code: OpCode,
951 recursion_desired: bool,
952 checking_disabled: bool,
953 queries: Vec<Query>,
954 dnssec_ok: bool,
955 client_subnet: Option<ClientSubnet>,
956}
957
958impl CacheKey {
959 fn from_request(request: &DnsRequest) -> Self {
960 let dnssec_ok;
961 let client_subnet;
962 if let Some(edns) = &request.edns {
963 dnssec_ok = edns.flags().dnssec_ok;
964 if let Some(EdnsOption::Subnet(subnet)) = edns.option(EdnsCode::Subnet) {
965 client_subnet = Some(*subnet);
966 } else {
967 client_subnet = None;
968 }
969 } else {
970 dnssec_ok = false;
971 client_subnet = None;
972 }
973 Self {
974 op_code: request.op_code,
975 recursion_desired: request.recursion_desired,
976 checking_disabled: request.checking_disabled,
977 queries: request.queries.clone(),
978 dnssec_ok,
979 client_subnet,
980 }
981 }
982}
983
984#[derive(Clone)]
985pub(crate) struct SharedLookup(Shared<BoxFuture<'static, Option<Result<DnsResponse, NetError>>>>);
986
987impl Future for SharedLookup {
988 type Output = Result<DnsResponse, NetError>;
989
990 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
991 self.0.poll_unpin(cx).map(|o| match o {
992 Some(r) => r,
993 None => Err("no response from nameserver".into()),
994 })
995 }
996}
997
998#[cfg(test)]
999#[cfg(feature = "tokio")]
1000mod tests {
1001 use std::collections::HashSet;
1002 use std::future::Future;
1003 use std::io;
1004 use std::net::{IpAddr, SocketAddr};
1005 use std::pin::Pin;
1006 use std::str::FromStr;
1007 use std::sync::atomic::{AtomicBool, Ordering};
1008 use std::thread;
1009 use std::time::Duration;
1010
1011 use futures_util::future;
1012 use test_support::{
1013 MockNetworkHandler, MockProvider, MockRecord, MockTcpStream, MockUdpSocket, subscribe,
1014 };
1015 use tokio::runtime::Runtime;
1016
1017 use super::*;
1018 use crate::config::{NameServerConfig, ResolverConfig, ServerOrderingStrategy};
1019 use crate::net::runtime::{RuntimeProvider, TokioHandle, TokioRuntimeProvider, TokioTime};
1020 use crate::net::xfer::{DnsHandle, FirstAnswer, Protocol};
1021 use crate::proto::op::{DnsRequestOptions, Message, Query};
1022 use crate::proto::rr::{DNSClass, Name, RecordType};
1023
1024 #[ignore]
1025 #[test]
1027 #[allow(clippy::uninlined_format_args)]
1028 fn test_failed_then_success_pool() {
1029 subscribe();
1030
1031 let mut config1 = NameServerConfig::udp(IpAddr::from([127, 0, 0, 252]));
1032 config1.trust_negative_responses = false;
1033 let config2 = NameServerConfig::udp(IpAddr::from([8, 8, 8, 8]));
1034
1035 let resolver_config = ResolverConfig::from_name_servers(vec![config1, config2]);
1036
1037 let io_loop = Runtime::new().unwrap();
1038 let pool = NameServerPool::from_config(
1039 resolver_config.name_servers,
1040 Arc::new(PoolContext::new(
1041 ResolverOpts::default(),
1042 TlsConfig::new().unwrap(),
1043 )),
1044 TokioRuntimeProvider::new(),
1045 );
1046
1047 let name = Name::parse("www.example.com.", None).unwrap();
1048
1049 for i in 0..2 {
1051 assert!(
1052 io_loop
1053 .block_on(
1054 pool.lookup(
1055 Query::query(name.clone(), RecordType::A),
1056 DnsRequestOptions::default()
1057 )
1058 .first_answer()
1059 )
1060 .is_err(),
1061 "iter: {}",
1062 i
1063 );
1064 }
1065
1066 for i in 0..10 {
1067 assert!(
1068 io_loop
1069 .block_on(
1070 pool.lookup(
1071 Query::query(name.clone(), RecordType::A),
1072 DnsRequestOptions::default()
1073 )
1074 .first_answer()
1075 )
1076 .is_ok(),
1077 "iter: {}",
1078 i
1079 );
1080 }
1081 }
1082
1083 #[tokio::test]
1084 async fn test_multi_use_conns() {
1085 subscribe();
1086
1087 let conn_provider = TokioRuntimeProvider::default();
1088 let opts = ResolverOpts {
1089 try_tcp_on_error: true,
1090 ..ResolverOpts::default()
1091 };
1092
1093 let tcp = NameServerConfig::tcp(IpAddr::from([8, 8, 8, 8]));
1094 let name_server = Arc::new(NameServer::new([], tcp, &opts, conn_provider));
1095 let name_servers = vec![name_server];
1096 let pool = NameServerPool::from_nameservers(
1097 name_servers.clone(),
1098 Arc::new(PoolContext::new(opts, TlsConfig::new().unwrap())),
1099 );
1100
1101 let name = Name::from_str("www.example.com.").unwrap();
1102
1103 let response = pool
1105 .lookup(
1106 Query::query(name.clone(), RecordType::A),
1107 DnsRequestOptions::default(),
1108 )
1109 .first_answer()
1110 .await
1111 .expect("lookup failed");
1112
1113 assert!(!response.answers.is_empty());
1114
1115 assert!(
1116 name_servers[0].is_connected(),
1117 "if this is failing then the NameServers aren't being properly shared."
1118 );
1119
1120 let response = pool
1122 .lookup(
1123 Query::query(name, RecordType::AAAA),
1124 DnsRequestOptions::default(),
1125 )
1126 .first_answer()
1127 .await
1128 .expect("lookup failed");
1129
1130 assert!(!response.answers.is_empty());
1131
1132 assert!(
1133 name_servers[0].is_connected(),
1134 "if this is failing then the NameServers aren't being properly shared."
1135 );
1136 }
1137
1138 #[tokio::test]
1145 async fn test_pool_retries_on_timeout() {
1146 subscribe();
1147
1148 let timeout_ip = IpAddr::from([10, 0, 0, 1]);
1149 let good_ip = IpAddr::from([10, 0, 0, 2]);
1150 let query_name = Name::from_str("example.com.").unwrap();
1151
1152 let responses = vec![MockRecord::a(good_ip, &query_name, good_ip)];
1154 let handler = MockNetworkHandler::new(responses);
1155 let mock_provider = MockProvider::new(handler);
1156
1157 let provider = TimeoutProvider::new(mock_provider, vec![timeout_ip]);
1159
1160 let opts = ResolverOpts {
1161 num_concurrent_reqs: 1,
1162 server_ordering_strategy: ServerOrderingStrategy::UserProvidedOrder,
1163 ..ResolverOpts::default()
1164 };
1165
1166 let pool = NameServerPool::from_nameservers(
1167 vec![
1168 Arc::new(NameServer::new(
1169 [].into_iter(),
1170 NameServerConfig::udp(timeout_ip),
1171 &opts,
1172 provider.clone(),
1173 )),
1174 Arc::new(NameServer::new(
1175 [].into_iter(),
1176 NameServerConfig::udp(good_ip),
1177 &opts,
1178 provider.clone(),
1179 )),
1180 ],
1181 Arc::new(PoolContext::new(opts, TlsConfig::new().unwrap())),
1182 );
1183
1184 let response = pool
1187 .lookup(
1188 Query::query(query_name.clone(), RecordType::A),
1189 DnsRequestOptions::default(),
1190 )
1191 .first_answer()
1192 .await
1193 .expect("pool should retry on timeout and succeed with the second server");
1194
1195 assert!(
1196 !response.answers.is_empty(),
1197 "expected A record in response"
1198 );
1199 }
1200
1201 #[tokio::test]
1204 async fn test_timeout_penalizes_server_srtt() {
1205 subscribe();
1206
1207 let timeout_ip = IpAddr::from([10, 0, 0, 1]);
1208 let good_ip = IpAddr::from([10, 0, 0, 2]);
1209 let query_name = Name::from_str("example.com.").unwrap();
1210
1211 let responses = vec![MockRecord::a(good_ip, &query_name, good_ip)];
1212 let handler = MockNetworkHandler::new(responses);
1213 let mock_provider = MockProvider::new(handler);
1214 let provider = TimeoutProvider::new(mock_provider, vec![timeout_ip]);
1215
1216 let opts = ResolverOpts {
1217 num_concurrent_reqs: 1,
1218 server_ordering_strategy: ServerOrderingStrategy::UserProvidedOrder,
1219 ..ResolverOpts::default()
1220 };
1221
1222 let ns_timeout = Arc::new(NameServer::new(
1223 [].into_iter(),
1224 NameServerConfig::udp(timeout_ip),
1225 &opts,
1226 provider.clone(),
1227 ));
1228 let ns_good = Arc::new(NameServer::new(
1229 [].into_iter(),
1230 NameServerConfig::udp(good_ip),
1231 &opts,
1232 provider.clone(),
1233 ));
1234
1235 let initial_srtt_timeout = ns_timeout.decayed_srtt();
1236
1237 let pool = NameServerPool::from_nameservers(
1238 vec![ns_timeout.clone(), ns_good.clone()],
1239 Arc::new(PoolContext::new(opts, TlsConfig::new().unwrap())),
1240 );
1241
1242 let _response = pool
1244 .lookup(
1245 Query::query(query_name.clone(), RecordType::A),
1246 DnsRequestOptions::default(),
1247 )
1248 .first_answer()
1249 .await
1250 .expect("lookup should succeed via second server");
1251
1252 assert!(
1254 ns_timeout.decayed_srtt() > initial_srtt_timeout,
1255 "timeout server SRTT should increase after failure: {} should be > {}",
1256 ns_timeout.decayed_srtt(),
1257 initial_srtt_timeout,
1258 );
1259
1260 let failure_penalty = 5_000_000.0_f64; assert!(
1265 ns_good.decayed_srtt() < failure_penalty,
1266 "good server SRTT should not be penalized: {}",
1267 ns_good.decayed_srtt(),
1268 );
1269 }
1270
1271 #[derive(Clone)]
1275 struct TimeoutProvider {
1276 inner: MockProvider,
1277 timeout_ips: Arc<HashSet<IpAddr>>,
1278 }
1279
1280 impl TimeoutProvider {
1281 fn new(inner: MockProvider, timeout_ips: Vec<IpAddr>) -> Self {
1282 Self {
1283 inner,
1284 timeout_ips: Arc::new(timeout_ips.into_iter().collect()),
1285 }
1286 }
1287 }
1288
1289 impl RuntimeProvider for TimeoutProvider {
1290 type Handle = TokioHandle;
1291 type Timer = TokioTime;
1292 type Udp = MockUdpSocket;
1293 type Tcp = MockTcpStream;
1294
1295 fn create_handle(&self) -> Self::Handle {
1296 self.inner.create_handle()
1297 }
1298
1299 fn connect_tcp(
1300 &self,
1301 server_addr: SocketAddr,
1302 bind_addr: Option<SocketAddr>,
1303 timeout: Option<Duration>,
1304 ) -> Pin<Box<dyn Future<Output = Result<Self::Tcp, io::Error>> + Send>> {
1305 if self.timeout_ips.contains(&server_addr.ip()) {
1306 Box::pin(future::ready(Err(io::Error::from(io::ErrorKind::TimedOut))))
1307 } else {
1308 self.inner.connect_tcp(server_addr, bind_addr, timeout)
1309 }
1310 }
1311
1312 fn bind_udp(
1313 &self,
1314 local_addr: SocketAddr,
1315 server_addr: SocketAddr,
1316 ) -> Pin<Box<dyn Future<Output = Result<Self::Udp, io::Error>> + Send>> {
1317 if self.timeout_ips.contains(&server_addr.ip()) {
1318 Box::pin(future::ready(Err(io::Error::from(io::ErrorKind::TimedOut))))
1319 } else {
1320 self.inner.bind_udp(local_addr, server_addr)
1321 }
1322 }
1323 }
1324
1325 #[tokio::test]
1334 async fn test_cancelled_parallel_server_is_penalized() {
1335 subscribe();
1336
1337 let unreachable_ip = IpAddr::from([10, 0, 0, 1]);
1338 let good_ip = IpAddr::from([10, 0, 0, 2]);
1339 let query_name = Name::from_str("example.com.").unwrap();
1340
1341 let responses = vec![MockRecord::a(good_ip, &query_name, good_ip)];
1342 let handler = MockNetworkHandler::new(responses);
1343 let mock_provider = MockProvider::new(handler);
1344 let provider = PendingProvider::new(mock_provider, vec![unreachable_ip]);
1345
1346 let opts = ResolverOpts {
1347 num_concurrent_reqs: 2,
1349 server_ordering_strategy: ServerOrderingStrategy::UserProvidedOrder,
1350 ..ResolverOpts::default()
1351 };
1352
1353 let ns_unreachable = Arc::new(NameServer::new(
1354 [].into_iter(),
1355 NameServerConfig::udp(unreachable_ip),
1356 &opts,
1357 provider.clone(),
1358 ));
1359 let ns_good = Arc::new(NameServer::new(
1360 [].into_iter(),
1361 NameServerConfig::udp(good_ip),
1362 &opts,
1363 provider.clone(),
1364 ));
1365
1366 let initial_srtt = ns_unreachable.decayed_srtt();
1367
1368 let pool = NameServerPool::from_nameservers(
1369 vec![ns_unreachable.clone(), ns_good.clone()],
1370 Arc::new(PoolContext::new(opts, TlsConfig::new().unwrap())),
1371 );
1372
1373 let _response = pool
1375 .lookup(
1376 Query::query(query_name.clone(), RecordType::A),
1377 DnsRequestOptions::default(),
1378 )
1379 .first_answer()
1380 .await
1381 .expect("lookup should succeed via good server");
1382
1383 assert!(
1386 ns_unreachable.decayed_srtt() > initial_srtt,
1387 "unreachable server SRTT should increase after being cancelled: {} should be > {}",
1388 ns_unreachable.decayed_srtt(),
1389 initial_srtt,
1390 );
1391
1392 let failure_penalty = 5_000_000.0_f64;
1394 assert!(
1395 ns_good.decayed_srtt() < failure_penalty,
1396 "good server SRTT should not be penalized: {}",
1397 ns_good.decayed_srtt(),
1398 );
1399 }
1400
1401 #[derive(Clone)]
1406 struct PendingProvider {
1407 inner: MockProvider,
1408 pending_ips: Arc<HashSet<IpAddr>>,
1409 }
1410
1411 impl PendingProvider {
1412 fn new(inner: MockProvider, pending_ips: Vec<IpAddr>) -> Self {
1413 Self {
1414 inner,
1415 pending_ips: Arc::new(pending_ips.into_iter().collect()),
1416 }
1417 }
1418 }
1419
1420 impl RuntimeProvider for PendingProvider {
1421 type Handle = TokioHandle;
1422 type Timer = TokioTime;
1423 type Udp = MockUdpSocket;
1424 type Tcp = MockTcpStream;
1425
1426 fn create_handle(&self) -> Self::Handle {
1427 self.inner.create_handle()
1428 }
1429
1430 fn connect_tcp(
1431 &self,
1432 server_addr: SocketAddr,
1433 bind_addr: Option<SocketAddr>,
1434 timeout: Option<Duration>,
1435 ) -> Pin<Box<dyn Future<Output = Result<Self::Tcp, io::Error>> + Send>> {
1436 if self.pending_ips.contains(&server_addr.ip()) {
1437 Box::pin(future::pending())
1438 } else {
1439 self.inner.connect_tcp(server_addr, bind_addr, timeout)
1440 }
1441 }
1442
1443 fn bind_udp(
1444 &self,
1445 local_addr: SocketAddr,
1446 server_addr: SocketAddr,
1447 ) -> Pin<Box<dyn Future<Output = Result<Self::Udp, io::Error>> + Send>> {
1448 if self.pending_ips.contains(&server_addr.ip()) {
1449 Box::pin(future::pending())
1450 } else {
1451 self.inner.bind_udp(local_addr, server_addr)
1452 }
1453 }
1454 }
1455
1456 #[test]
1466 fn test_sort_by_decayed_srtt_does_not_panic() {
1467 let opts = ResolverOpts::default();
1468 let mock_provider = MockProvider::new(MockNetworkHandler::new(vec![]));
1469
1470 let mut servers = (1..=50)
1471 .map(|i| {
1472 let ns = Arc::new(NameServer::new(
1473 [],
1474 NameServerConfig::udp(IpAddr::from([10, 0, 0, i])),
1475 &opts,
1476 mock_provider.clone(),
1477 ));
1478 ns.test_record_failure();
1481 ns
1482 })
1483 .collect::<Vec<_>>();
1484
1485 let servers_writer = servers.clone();
1488 let stop = Arc::new(AtomicBool::new(false));
1489 let stop_writer = stop.clone();
1490 let writer = thread::spawn(move || {
1491 while !stop_writer.load(Ordering::Relaxed) {
1492 for s in &servers_writer {
1493 s.test_record_failure();
1494 }
1495 }
1496 });
1497
1498 struct StopGuard(Arc<AtomicBool>);
1500 impl Drop for StopGuard {
1501 fn drop(&mut self) {
1502 self.0.store(true, Ordering::Relaxed);
1503 }
1504 }
1505 let _guard = StopGuard(stop.clone());
1506
1507 for _ in 0..100_000 {
1512 sort_servers_by_query_statistics(&mut servers);
1513 }
1514
1515 stop.store(true, Ordering::Relaxed);
1516 writer.join().unwrap();
1517 }
1518
1519 #[tokio::test]
1520 async fn test_pool_foreign_class_records() {
1521 subscribe();
1522
1523 struct TestCase {
1524 name: &'static str,
1525 section: ForeignSection,
1526 class: Option<DNSClass>,
1527 expect_err: bool,
1528 }
1529
1530 let cases = [
1531 TestCase {
1532 name: "foreign class CH in answers is rejected",
1533 section: ForeignSection::Answer,
1534 class: Some(DNSClass::CH),
1535 expect_err: true,
1536 },
1537 TestCase {
1538 name: "foreign class CH in authorities is rejected",
1539 section: ForeignSection::Authority,
1540 class: Some(DNSClass::CH),
1541 expect_err: true,
1542 },
1543 TestCase {
1544 name: "foreign class HS in additionals is rejected",
1545 section: ForeignSection::Additional,
1546 class: Some(DNSClass::HS),
1547 expect_err: true,
1548 },
1549 TestCase {
1550 name: "clean IN-only response passes through",
1551 section: ForeignSection::None,
1552 class: None,
1553 expect_err: false,
1554 },
1555 ];
1556
1557 for case in cases {
1558 let result = run_foreign_class_lookup(case.section, case.class).await;
1559 match (case.expect_err, result) {
1560 (true, Err(NetError::ForeignClassRecord { record_class, .. })) => {
1561 assert_eq!(
1562 Some(record_class),
1563 case.class,
1564 "{}: unexpected record_class",
1565 case.name
1566 );
1567 }
1568 (true, other) => {
1569 panic!(
1570 "{}: expected ForeignClassRecord error, got {other:?}",
1571 case.name
1572 )
1573 }
1574 (false, Ok(response)) => assert!(
1575 !response.answers.is_empty(),
1576 "{}: expected non-empty answer",
1577 case.name
1578 ),
1579 (false, Err(e)) => panic!("{}: expected success, got error {e:?}", case.name),
1580 }
1581 }
1582 }
1583
1584 async fn run_foreign_class_lookup(
1585 section: ForeignSection,
1586 class: Option<DNSClass>,
1587 ) -> Result<DnsResponse, NetError> {
1588 let server_ip = IpAddr::from([10, 0, 0, 1]);
1589 let query_name = Name::from_str("example.com.")?;
1590 let target_name = query_name.clone();
1591
1592 let handler =
1593 MockNetworkHandler::new(vec![MockRecord::a(server_ip, &query_name, server_ip)])
1594 .with_mutation(Box::new(
1595 move |_destination: IpAddr, _protocol: Protocol, msg: &mut Message| {
1596 let Some(class) = class else { return };
1597 if msg.queries.first().map(|q| &q.name) != Some(&target_name) {
1598 return;
1599 }
1600 let mut record = Record::from_rdata(
1601 target_name.clone(),
1602 300,
1603 RData::A(A([6, 6, 6, 6].into())),
1604 );
1605 record.dns_class = class;
1606 match section {
1607 ForeignSection::Answer => {
1608 msg.add_answer(record);
1609 }
1610 ForeignSection::Authority => {
1611 msg.add_authority(record);
1612 }
1613 ForeignSection::Additional => {
1614 msg.add_additional(record);
1615 }
1616 ForeignSection::None => {}
1617 }
1618 },
1619 ));
1620
1621 let pool = NameServerPool::from_nameservers(
1622 vec![Arc::new(NameServer::new(
1623 [].into_iter(),
1624 NameServerConfig::udp(server_ip),
1625 &ResolverOpts::default(),
1626 MockProvider::new(handler),
1627 ))],
1628 Arc::new(PoolContext::new(ResolverOpts::default(), TlsConfig::new()?)),
1629 );
1630
1631 pool.lookup(
1632 Query::query(query_name, RecordType::A),
1633 DnsRequestOptions::default(),
1634 )
1635 .first_answer()
1636 .await
1637 }
1638
1639 #[derive(Clone, Copy)]
1640 enum ForeignSection {
1641 Answer,
1642 Authority,
1643 Additional,
1644 None,
1645 }
1646
1647 #[tokio::test]
1654 async fn test_truncated_tcp_only_no_infinite_retry() {
1655 subscribe();
1656
1657 let server_ip = IpAddr::from([10, 0, 0, 1]);
1658 let query_name = Name::from_str("example.com.").unwrap();
1659
1660 let responses = vec![MockRecord::a(server_ip, &query_name, server_ip)];
1661 let handler = MockNetworkHandler::new(responses).with_mutation(Box::new(
1662 |_destination: IpAddr, _protocol: Protocol, msg: &mut Message| {
1663 msg.metadata.truncation = true;
1664 },
1665 ));
1666 let provider = MockProvider::new(handler);
1667
1668 let opts = ResolverOpts {
1669 num_concurrent_reqs: 1,
1670 server_ordering_strategy: ServerOrderingStrategy::UserProvidedOrder,
1671 ..ResolverOpts::default()
1672 };
1673
1674 let pool = NameServerPool::from_nameservers(
1675 vec![Arc::new(NameServer::new(
1676 [].into_iter(),
1677 NameServerConfig::tcp(server_ip),
1678 &opts,
1679 provider.clone(),
1680 ))],
1681 Arc::new(PoolContext::new(opts, TlsConfig::new().unwrap())),
1682 );
1683
1684 let result = pool
1685 .lookup(
1686 Query::query(query_name.clone(), RecordType::A),
1687 DnsRequestOptions::default(),
1688 )
1689 .first_answer()
1690 .await;
1691
1692 assert!(
1693 matches!(result, Err(NetError::Truncated)),
1694 "expected Truncated error, got: {result:?}"
1695 );
1696
1697 let queries = provider.queries(&server_ip);
1701 assert!(
1702 queries.len() <= 2,
1703 "TCP-only server should not be retried more than once, got {} queries",
1704 queries.len()
1705 );
1706 }
1707
1708 #[tokio::test]
1712 async fn test_truncated_udp_then_tcp_no_infinite_retry() {
1713 subscribe();
1714
1715 let server_ip = IpAddr::from([10, 0, 0, 1]);
1716 let query_name = Name::from_str("example.com.").unwrap();
1717
1718 let responses = vec![MockRecord::a(server_ip, &query_name, server_ip)];
1719 let handler = MockNetworkHandler::new(responses).with_mutation(Box::new(
1721 |_destination: IpAddr, _protocol: Protocol, msg: &mut Message| {
1722 msg.metadata.truncation = true;
1723 },
1724 ));
1725 let provider = MockProvider::new(handler);
1726
1727 let opts = ResolverOpts {
1728 num_concurrent_reqs: 1,
1729 server_ordering_strategy: ServerOrderingStrategy::UserProvidedOrder,
1730 ..ResolverOpts::default()
1731 };
1732
1733 let pool = NameServerPool::from_nameservers(
1734 vec![Arc::new(NameServer::new(
1735 [].into_iter(),
1736 NameServerConfig::udp_and_tcp(server_ip),
1737 &opts,
1738 provider.clone(),
1739 ))],
1740 Arc::new(PoolContext::new(opts, TlsConfig::new().unwrap())),
1741 );
1742
1743 let result = pool
1744 .lookup(
1745 Query::query(query_name.clone(), RecordType::A),
1746 DnsRequestOptions::default(),
1747 )
1748 .first_answer()
1749 .await;
1750
1751 assert!(
1752 matches!(result, Err(NetError::Truncated)),
1753 "expected Truncated error, got: {result:?}"
1754 );
1755
1756 let queries = provider.queries(&server_ip);
1758 assert!(
1759 queries.len() <= 2,
1760 "server should not be retried more than once after TCP truncation, got {} queries",
1761 queries.len()
1762 );
1763 }
1764
1765 #[tokio::test]
1769 async fn test_truncated_multiple_servers_no_retry_storm() {
1770 subscribe();
1771
1772 let server1_ip = IpAddr::from([10, 0, 0, 1]);
1773 let server2_ip = IpAddr::from([10, 0, 0, 2]);
1774 let query_name = Name::from_str("example.com.").unwrap();
1775
1776 let responses = vec![
1777 MockRecord::a(server1_ip, &query_name, server1_ip),
1778 MockRecord::a(server2_ip, &query_name, server2_ip),
1779 ];
1780 let handler = MockNetworkHandler::new(responses).with_mutation(Box::new(
1781 |_destination: IpAddr, _protocol: Protocol, msg: &mut Message| {
1782 msg.metadata.truncation = true;
1783 },
1784 ));
1785 let provider = MockProvider::new(handler);
1786
1787 let opts = ResolverOpts {
1788 num_concurrent_reqs: 1,
1789 server_ordering_strategy: ServerOrderingStrategy::UserProvidedOrder,
1790 ..ResolverOpts::default()
1791 };
1792
1793 let pool = NameServerPool::from_nameservers(
1794 vec![
1795 Arc::new(NameServer::new(
1796 [].into_iter(),
1797 NameServerConfig::udp_and_tcp(server1_ip),
1798 &opts,
1799 provider.clone(),
1800 )),
1801 Arc::new(NameServer::new(
1802 [].into_iter(),
1803 NameServerConfig::udp_and_tcp(server2_ip),
1804 &opts,
1805 provider.clone(),
1806 )),
1807 ],
1808 Arc::new(PoolContext::new(opts, TlsConfig::new().unwrap())),
1809 );
1810
1811 let result = pool
1812 .lookup(
1813 Query::query(query_name.clone(), RecordType::A),
1814 DnsRequestOptions::default(),
1815 )
1816 .first_answer()
1817 .await;
1818
1819 assert!(
1820 matches!(result, Err(NetError::Truncated)),
1821 "expected Truncated error, got: {result:?}"
1822 );
1823
1824 let total_queries =
1825 provider.queries(&server1_ip).len() + provider.queries(&server2_ip).len();
1826 assert!(
1827 total_queries <= 3,
1828 "total queries across all servers should be bounded, got {total_queries}"
1829 );
1830 }
1831}