1use std::fmt;
10use std::future::Future;
11use std::net::IpAddr;
12use std::pin::Pin;
13use std::sync::Arc;
14use std::task::{Context, Poll};
15
16use futures_util::{FutureExt, future};
17use tracing::debug;
18
19use crate::caching_client::CachingClient;
20use crate::config::{ResolveHosts, ResolverConfig, ResolverOpts};
21use crate::dns_lru::{self, DnsLru, TtlConfig};
22use crate::error::{ResolveError, ResolveErrorKind};
23use crate::hosts::Hosts;
24use crate::lookup::{self, Lookup, LookupEither};
25use crate::lookup_ip::{LookupIp, LookupIpFuture};
26#[cfg(feature = "tokio")]
27use crate::name_server::TokioConnectionProvider;
28use crate::name_server::{ConnectionProvider, NameServerPool};
29#[cfg(feature = "__dnssec")]
30use crate::proto::dnssec::{DnssecDnsHandle, TrustAnchors};
31use crate::proto::op::Query;
32use crate::proto::rr::domain::usage::ONION;
33use crate::proto::rr::{IntoName, Name, RData, Record, RecordType};
34use crate::proto::xfer::{DnsHandle, DnsRequestOptions, RetryDnsHandle};
35
36pub struct ResolverBuilder<P> {
40 config: ResolverConfig,
41 options: ResolverOpts,
42 provider: P,
43
44 #[cfg(feature = "__dnssec")]
45 trust_anchor: Option<Arc<TrustAnchors>>,
46}
47
48impl<P> ResolverBuilder<P>
49where
50 P: ConnectionProvider,
51{
52 pub fn with_options(mut self, options: ResolverOpts) -> Self {
61 self.options = options;
62 self
63 }
64
65 pub fn options_mut(&mut self) -> &mut ResolverOpts {
67 &mut self.options
68 }
69
70 #[cfg(feature = "__dnssec")]
72 pub fn with_trust_anchor(mut self, trust_anchor: Arc<TrustAnchors>) -> Self {
73 self.trust_anchor = Some(trust_anchor);
74 self
75 }
76
77 pub fn build(self) -> Resolver<P> {
79 let Self {
80 config,
81 options,
82 provider,
83 #[cfg(feature = "__dnssec")]
84 trust_anchor,
85 } = self;
86
87 let pool = NameServerPool::from_config_with_provider(&config, options.clone(), provider);
88 let client = RetryDnsHandle::new(pool, options.attempts);
89 let either;
90
91 if options.validate {
92 #[cfg(feature = "__dnssec")]
93 {
94 let trust_anchor =
95 trust_anchor.unwrap_or_else(|| Arc::new(TrustAnchors::default()));
96 either =
97 LookupEither::Secure(DnssecDnsHandle::with_trust_anchor(client, trust_anchor));
98 }
99
100 #[cfg(not(feature = "__dnssec"))]
101 {
102 tracing::warn!("validate option is only available with dnssec features");
103 either = LookupEither::Retry(client);
104 }
105 } else {
106 either = LookupEither::Retry(client);
107 }
108
109 let lru = DnsLru::new(options.cache_size, TtlConfig::from_opts(&options));
110 let client_cache = CachingClient::with_cache(lru, either, options.preserve_intermediates);
111
112 let hosts = Arc::new(match options.use_hosts_file {
113 ResolveHosts::Always | ResolveHosts::Auto => Hosts::from_system().unwrap_or_default(),
114 ResolveHosts::Never => Hosts::default(),
115 });
116
117 Resolver {
118 config,
119 options,
120 client_cache,
121 hosts,
122 }
123 }
124}
125
126#[derive(Clone)]
140pub struct Resolver<P: ConnectionProvider> {
141 config: ResolverConfig,
142 options: ResolverOpts,
143 client_cache: CachingClient<LookupEither<P>>,
144 hosts: Arc<Hosts>,
145}
146
147#[cfg(feature = "tokio")]
149pub type TokioResolver = Resolver<TokioConnectionProvider>;
150
151macro_rules! lookup_fn {
152 ($p:ident, $l:ty, $r:path) => {
153 pub async fn $p<N: IntoName>(&self, query: N) -> Result<$l, ResolveError> {
161 let name = match query.into_name() {
162 Ok(name) => name,
163 Err(err) => {
164 return Err(err.into());
165 }
166 };
167
168 self.inner_lookup(name, $r, self.request_options()).await
169 }
170 };
171 ($p:ident, $l:ty, $r:path, $t:ty) => {
172 pub async fn $p(&self, query: $t) -> Result<$l, ResolveError> {
178 let name = Name::from(query);
179 self.inner_lookup(name, $r, self.request_options()).await
180 }
181 };
182}
183
184#[cfg(feature = "tokio")]
185impl TokioResolver {
186 #[cfg(any(unix, target_os = "windows"))]
190 #[cfg(feature = "system-config")]
191 pub fn builder_tokio() -> Result<ResolverBuilder<TokioConnectionProvider>, ResolveError> {
192 Self::builder(TokioConnectionProvider::default())
193 }
194}
195
196impl<R: ConnectionProvider> Resolver<R> {
197 #[cfg(any(unix, target_os = "windows"))]
204 #[cfg(feature = "system-config")]
205 pub fn builder(provider: R) -> Result<ResolverBuilder<R>, ResolveError> {
206 let (config, options) = super::system_conf::read_system_conf()?;
207 let mut builder = Self::builder_with_config(config, provider);
208 *builder.options_mut() = options;
209 Ok(builder)
210 }
211
212 pub fn builder_with_config(config: ResolverConfig, provider: R) -> ResolverBuilder<R> {
214 ResolverBuilder {
215 config,
216 options: ResolverOpts::default(),
217 provider,
218 #[cfg(feature = "__dnssec")]
219 trust_anchor: None,
220 }
221 }
222
223 pub fn clear_cache(&self) {
225 self.client_cache.clear_cache();
226 }
227
228 pub fn config(&self) -> &ResolverConfig {
230 &self.config
231 }
232
233 pub fn options(&self) -> &ResolverOpts {
235 &self.options
236 }
237}
238
239impl<P: ConnectionProvider> Resolver<P> {
240 pub(crate) fn request_options(&self) -> DnsRequestOptions {
242 let mut request_opts = DnsRequestOptions::default();
243 request_opts.recursion_desired = self.options.recursion_desired;
244 request_opts.use_edns = self.options.edns0;
245 request_opts.case_randomization = self.options.case_randomization;
246
247 request_opts
248 }
249
250 pub async fn lookup<N: IntoName>(
263 &self,
264 name: N,
265 record_type: RecordType,
266 ) -> Result<Lookup, ResolveError> {
267 let name = match name.into_name() {
268 Ok(name) => name,
269 Err(err) => return Err(err.into()),
270 };
271
272 self.inner_lookup(name, record_type, self.request_options())
273 .await
274 }
275
276 fn push_name(name: Name, names: &mut Vec<Name>) {
277 if !names.contains(&name) {
278 names.push(name);
279 }
280 }
281
282 fn build_names(&self, name: Name) -> Vec<Name> {
283 if name.is_fqdn()
285 || ONION.zone_of(&name)
286 && name
287 .trim_to(2)
288 .iter()
289 .next()
290 .map(|name| name.len() == 56) .unwrap_or(false)
292 {
293 vec![name]
296 } else {
297 let mut names =
300 Vec::<Name>::with_capacity(1 + 1 + self.config.search().len());
301
302 let raw_name_first: bool =
304 name.num_labels() as usize > self.options.ndots || name.is_localhost();
305
306 if !raw_name_first {
308 let mut fqdn = name.clone();
309 fqdn.set_fqdn(true);
310 names.push(fqdn);
311 }
312
313 for search in self.config.search().iter().rev() {
314 let name_search = name.clone().append_domain(search);
315
316 match name_search {
317 Ok(name_search) => Self::push_name(name_search, &mut names),
318 Err(e) => debug!(
319 "Not adding {} to {} for search due to error: {}",
320 search, name, e
321 ),
322 }
323 }
324
325 if let Some(domain) = self.config.domain() {
326 let name_search = name.clone().append_domain(domain);
327
328 match name_search {
329 Ok(name_search) => Self::push_name(name_search, &mut names),
330 Err(e) => debug!(
331 "Not adding {} to {} for search due to error: {}",
332 domain, name, e
333 ),
334 }
335 }
336
337 if raw_name_first {
339 let mut fqdn = name.clone();
341 fqdn.set_fqdn(true);
342 names.push(fqdn);
343 }
344
345 names
346 }
347 }
348
349 pub(crate) async fn inner_lookup<L>(
350 &self,
351 name: Name,
352 record_type: RecordType,
353 options: DnsRequestOptions,
354 ) -> Result<L, ResolveError>
355 where
356 L: From<Lookup> + Send + Sync + 'static,
357 {
358 let names = self.build_names(name);
359 LookupFuture::lookup_with_hosts(
360 names,
361 record_type,
362 options,
363 self.client_cache.clone(),
364 self.hosts.clone(),
365 )
366 .await
367 .map(L::from)
368 }
369
370 pub async fn lookup_ip(&self, host: impl IntoName) -> Result<LookupIp, ResolveError> {
377 let mut finally_ip_addr = None;
378 let maybe_ip = host.to_ip().map(RData::from);
379 let maybe_name = host.into_name();
380
381 if let Some(ip_addr) = maybe_ip {
383 let name = maybe_name.clone().unwrap_or_default();
384 let record = Record::from_rdata(name.clone(), dns_lru::MAX_TTL, ip_addr.clone());
385
386 if self.options.ndots > 4 {
391 finally_ip_addr = Some(record);
392 } else {
393 let query = Query::query(name, ip_addr.record_type());
394 let lookup = Lookup::new_with_max_ttl(query, Arc::from([record]));
395 return Ok(lookup.into());
396 }
397 }
398
399 let name = match (maybe_name, finally_ip_addr.as_ref()) {
400 (Ok(name), _) => name,
401 (Err(_), Some(ip_addr)) => {
402 let query = Query::query(ip_addr.name().clone(), ip_addr.record_type());
404 let lookup = Lookup::new_with_max_ttl(query, Arc::from([ip_addr.clone()]));
405 return Ok(lookup.into());
406 }
407 (Err(err), None) => {
408 return Err(err.into());
409 }
410 };
411
412 let names = self.build_names(name);
413 let hosts = self.hosts.clone();
414
415 LookupIpFuture::lookup(
416 names,
417 self.options.ip_strategy,
418 self.client_cache.clone(),
419 self.request_options(),
420 hosts,
421 finally_ip_addr.map(Record::into_data),
422 )
423 .await
424 }
425
426 pub fn set_hosts(&mut self, hosts: Arc<Hosts>) {
428 self.hosts = hosts;
429 }
430
431 lookup_fn!(
432 reverse_lookup,
433 lookup::ReverseLookup,
434 RecordType::PTR,
435 IpAddr
436 );
437 lookup_fn!(ipv4_lookup, lookup::Ipv4Lookup, RecordType::A);
438 lookup_fn!(ipv6_lookup, lookup::Ipv6Lookup, RecordType::AAAA);
439 lookup_fn!(mx_lookup, lookup::MxLookup, RecordType::MX);
440 lookup_fn!(ns_lookup, lookup::NsLookup, RecordType::NS);
441 lookup_fn!(soa_lookup, lookup::SoaLookup, RecordType::SOA);
442 lookup_fn!(srv_lookup, lookup::SrvLookup, RecordType::SRV);
443 lookup_fn!(tlsa_lookup, lookup::TlsaLookup, RecordType::TLSA);
444 lookup_fn!(txt_lookup, lookup::TxtLookup, RecordType::TXT);
445 lookup_fn!(cert_lookup, lookup::CertLookup, RecordType::CERT);
446}
447
448impl<P: ConnectionProvider> fmt::Debug for Resolver<P> {
449 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
450 f.debug_struct("Resolver").finish()
451 }
452}
453
454#[doc(hidden)]
456pub struct LookupFuture<C>
457where
458 C: DnsHandle + 'static,
459{
460 client_cache: CachingClient<C>,
461 names: Vec<Name>,
462 record_type: RecordType,
463 options: DnsRequestOptions,
464 query: Pin<Box<dyn Future<Output = Result<Lookup, ResolveError>> + Send>>,
465}
466
467impl<C> LookupFuture<C>
468where
469 C: DnsHandle + 'static,
470{
471 #[doc(hidden)]
479 pub fn lookup(
480 names: Vec<Name>,
481 record_type: RecordType,
482 options: DnsRequestOptions,
483 client_cache: CachingClient<C>,
484 ) -> Self {
485 Self::lookup_with_hosts(
486 names,
487 record_type,
488 options,
489 client_cache,
490 Arc::new(Hosts::default()),
491 )
492 }
493
494 #[doc(hidden)]
504 pub fn lookup_with_hosts(
505 mut names: Vec<Name>,
506 record_type: RecordType,
507 options: DnsRequestOptions,
508 client_cache: CachingClient<C>,
509 hosts: Arc<Hosts>,
510 ) -> Self {
511 let name = names.pop().ok_or_else(|| {
512 ResolveError::from(ResolveErrorKind::Message("can not lookup for no names"))
513 });
514
515 let query: Pin<Box<dyn Future<Output = Result<Lookup, ResolveError>> + Send>> = match name {
516 Ok(name) => {
517 let query = Query::query(name, record_type);
518
519 if let Some(lookup) = hosts.lookup_static_host(&query) {
520 future::ok(lookup).boxed()
521 } else {
522 client_cache.lookup(query, options).boxed()
523 }
524 }
525 Err(err) => future::err(err).boxed(),
526 };
527
528 Self {
529 client_cache,
530 names,
531 record_type,
532 options,
533 query,
534 }
535 }
536}
537
538impl<C> Future for LookupFuture<C>
539where
540 C: DnsHandle + 'static,
541{
542 type Output = Result<Lookup, ResolveError>;
543
544 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
545 loop {
546 let query = self.query.as_mut().poll_unpin(cx);
548
549 let should_retry = match &query {
551 Poll::Pending => return Poll::Pending,
553 Poll::Ready(Ok(lookup)) => lookup.records().is_empty(),
557 Poll::Ready(Err(_)) => true,
559 };
560
561 if should_retry {
562 if let Some(name) = self.names.pop() {
563 let record_type = self.record_type;
564 let options = self.options;
565
566 self.query = self
569 .client_cache
570 .lookup(Query::query(name, record_type), options);
571 continue;
574 }
575 }
576 return query;
580 }
585 }
586}
587
588#[cfg(all(test, feature = "tokio"))]
590pub(crate) mod testing {
591 use std::{net::*, str::FromStr};
592
593 use crate::Resolver;
594 use crate::config::{LookupIpStrategy, NameServerConfig, ResolverConfig};
595 use crate::name_server::ConnectionProvider;
596 use crate::proto::{rr::Name, runtime::Executor};
597
598 pub(crate) async fn lookup_test<R: ConnectionProvider>(config: ResolverConfig, handle: R) {
600 let resolver = Resolver::<R>::builder_with_config(config, handle).build();
601
602 let response = resolver
603 .lookup_ip("www.example.com.")
604 .await
605 .expect("failed to run lookup");
606
607 assert_ne!(response.iter().count(), 0);
608 }
609
610 pub(crate) async fn ip_lookup_test<R: ConnectionProvider>(handle: R) {
612 let resolver =
613 Resolver::<R>::builder_with_config(ResolverConfig::default(), handle).build();
614
615 let response = resolver
616 .lookup_ip("10.1.0.2")
617 .await
618 .expect("failed to run lookup");
619
620 assert_eq!(
621 Some(IpAddr::V4(Ipv4Addr::new(10, 1, 0, 2))),
622 response.iter().next()
623 );
624
625 let response = resolver
626 .lookup_ip("2606:2800:21f:cb07:6820:80da:af6b:8b2c")
627 .await
628 .expect("failed to run lookup");
629
630 assert_eq!(
631 Some(IpAddr::V6(Ipv6Addr::new(
632 0x2606, 0x2800, 0x21f, 0xcb07, 0x6820, 0x80da, 0xaf6b, 0x8b2c,
633 ))),
634 response.iter().next()
635 );
636 }
637
638 pub(crate) fn ip_lookup_across_threads_test<E: Executor, R: ConnectionProvider>(handle: R) {
640 use std::thread;
644 let resolver =
645 Resolver::<R>::builder_with_config(ResolverConfig::default(), handle).build();
646
647 let resolver_one = resolver.clone();
648 let resolver_two = resolver;
649
650 let test_fn = |resolver: Resolver<R>| {
651 let mut exec = E::new();
652
653 let response = exec
654 .block_on(resolver.lookup_ip("10.1.0.2"))
655 .expect("failed to run lookup");
656
657 assert_eq!(
658 Some(IpAddr::V4(Ipv4Addr::new(10, 1, 0, 2))),
659 response.iter().next()
660 );
661
662 let response = exec
663 .block_on(resolver.lookup_ip("2606:2800:21f:cb07:6820:80da:af6b:8b2c"))
664 .expect("failed to run lookup");
665
666 assert_eq!(
667 Some(IpAddr::V6(Ipv6Addr::new(
668 0x2606, 0x2800, 0x21f, 0xcb07, 0x6820, 0x80da, 0xaf6b, 0x8b2c,
669 ))),
670 response.iter().next()
671 );
672 };
673
674 let thread_one = thread::spawn(move || {
675 test_fn(resolver_one);
676 });
677
678 let thread_two = thread::spawn(move || {
679 test_fn(resolver_two);
680 });
681
682 thread_one.join().expect("thread_one failed");
683 thread_two.join().expect("thread_two failed");
684 }
685
686 #[cfg(feature = "__dnssec")]
688 pub(crate) async fn sec_lookup_test<R: ConnectionProvider>(handle: R) {
689 let mut resolver_builder = Resolver::builder_with_config(ResolverConfig::default(), handle);
690 resolver_builder.options_mut().validate = true;
691 resolver_builder.options_mut().try_tcp_on_error = true;
692 let resolver = resolver_builder.build();
693
694 let response = resolver
695 .lookup_ip("www.example.com.")
696 .await
697 .expect("failed to run lookup");
698
699 assert_ne!(response.iter().count(), 0);
700 assert!(
701 response
702 .as_lookup()
703 .record_iter()
704 .any(|record| record.proof().is_secure())
705 );
706 }
707
708 #[allow(deprecated)]
710 #[cfg(feature = "__dnssec")]
711 pub(crate) async fn sec_lookup_fails_test<R: ConnectionProvider>(handle: R) {
712 let mut resolver_builder = Resolver::builder_with_config(ResolverConfig::default(), handle);
713 resolver_builder.options_mut().validate = true;
714 resolver_builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4Only;
715 let resolver = resolver_builder.build();
716
717 let response = resolver.lookup_ip("hickory-dns.org.").await;
719
720 let lookup_ip = response.unwrap();
721 for record in lookup_ip.as_lookup().record_iter() {
722 assert!(record.proof().is_insecure());
723 }
724 }
725
726 #[cfg(feature = "system-config")]
728 pub(crate) async fn system_lookup_test<R: ConnectionProvider>(handle: R) {
729 let resolver = Resolver::<R>::builder(handle)
730 .expect("failed to create resolver")
731 .build();
732
733 let response = resolver
734 .lookup_ip("www.example.com.")
735 .await
736 .expect("failed to run lookup");
737
738 assert_eq!(response.iter().count(), 2);
739 for address in response.iter() {
740 if address.is_ipv4() {
741 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 215, 14)));
742 } else {
743 assert_eq!(
744 address,
745 IpAddr::V6(Ipv6Addr::new(
746 0x2606, 0x2800, 0x21f, 0xcb07, 0x6820, 0x80da, 0xaf6b, 0x8b2c,
747 ))
748 );
749 }
750 }
751 }
752
753 #[cfg(feature = "system-config")]
755 pub(crate) async fn hosts_lookup_test<R: ConnectionProvider>(handle: R) {
756 let resolver = Resolver::<R>::builder(handle)
757 .expect("failed to create resolver")
758 .build();
759
760 let response = resolver
761 .lookup_ip("a.com")
762 .await
763 .expect("failed to run lookup");
764
765 assert_eq!(response.iter().count(), 1);
766 for address in response.iter() {
767 if address.is_ipv4() {
768 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(10, 1, 0, 104)));
769 } else {
770 panic!("failed to run lookup");
771 }
772 }
773 }
774
775 pub(crate) async fn fqdn_test<R: ConnectionProvider>(handle: R) {
777 let domain = Name::from_str("incorrect.example.com.").unwrap();
778 let search = vec![
779 Name::from_str("bad.example.com.").unwrap(),
780 Name::from_str("wrong.example.com.").unwrap(),
781 ];
782 let name_servers: Vec<NameServerConfig> =
783 ResolverConfig::default().name_servers().to_owned();
784
785 let mut resolver_builder = Resolver::<R>::builder_with_config(
786 ResolverConfig::from_parts(Some(domain), search, name_servers),
787 handle,
788 );
789 resolver_builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4Only;
790 let resolver = resolver_builder.build();
791
792 let response = resolver
793 .lookup_ip("www.example.com.")
794 .await
795 .expect("failed to run lookup");
796
797 assert_ne!(response.iter().count(), 0);
798 for address in response.iter() {
799 assert!(address.is_ipv4(), "should only be looking up IPv4");
800 }
801 }
802
803 pub(crate) async fn ndots_test<R: ConnectionProvider>(handle: R) {
805 let domain = Name::from_str("incorrect.example.com.").unwrap();
806 let search = vec![
807 Name::from_str("bad.example.com.").unwrap(),
808 Name::from_str("wrong.example.com.").unwrap(),
809 ];
810 let name_servers: Vec<NameServerConfig> =
811 ResolverConfig::default().name_servers().to_owned();
812
813 let mut resolver_builder = Resolver::<R>::builder_with_config(
814 ResolverConfig::from_parts(Some(domain), search, name_servers),
815 handle,
816 );
817 resolver_builder.options_mut().ndots = 2;
819 resolver_builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4Only;
820 let resolver = resolver_builder.build();
821
822 let response = resolver
824 .lookup_ip("www.example.com")
825 .await
826 .expect("failed to run lookup");
827
828 assert_ne!(response.iter().count(), 0);
829 for address in response.iter() {
830 assert!(address.is_ipv4(), "should only be looking up IPv4");
831 }
832 }
833
834 pub(crate) async fn large_ndots_test<R: ConnectionProvider>(handle: R) {
836 let domain = Name::from_str("incorrect.example.com.").unwrap();
837 let search = vec![
838 Name::from_str("bad.example.com.").unwrap(),
839 Name::from_str("wrong.example.com.").unwrap(),
840 ];
841 let name_servers: Vec<NameServerConfig> =
842 ResolverConfig::default().name_servers().to_owned();
843
844 let mut resolver_builder = Resolver::<R>::builder_with_config(
845 ResolverConfig::from_parts(Some(domain), search, name_servers),
846 handle,
847 );
848 resolver_builder.options_mut().ndots = 5;
850 resolver_builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4Only;
851 let resolver = resolver_builder.build();
852
853 let response = resolver
855 .lookup_ip("www.example.com")
856 .await
857 .expect("failed to run lookup");
858
859 assert_ne!(response.iter().count(), 0);
860 for address in response.iter() {
861 assert!(address.is_ipv4(), "should only be looking up IPv4");
862 }
863 }
864
865 pub(crate) async fn domain_search_test<R: ConnectionProvider>(handle: R) {
867 let domain = Name::from_str("example.com.").unwrap();
869 let search = vec![
870 Name::from_str("bad.example.com.").unwrap(),
871 Name::from_str("wrong.example.com.").unwrap(),
872 ];
873 let name_servers: Vec<NameServerConfig> =
874 ResolverConfig::default().name_servers().to_owned();
875
876 let mut resolver_builder = Resolver::<R>::builder_with_config(
877 ResolverConfig::from_parts(Some(domain), search, name_servers),
878 handle,
879 );
880 resolver_builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4Only;
881 let resolver = resolver_builder.build();
882
883 let response = resolver
885 .lookup_ip("www")
886 .await
887 .expect("failed to run lookup");
888
889 assert_ne!(response.iter().count(), 0);
890 for address in response.iter() {
891 assert!(address.is_ipv4(), "should only be looking up IPv4");
892 }
893 }
894
895 pub(crate) async fn search_list_test<R: ConnectionProvider>(handle: R) {
897 let domain = Name::from_str("incorrect.example.com.").unwrap();
898 let search = vec![
899 Name::from_str("bad.example.com.").unwrap(),
901 Name::from_str("example.com.").unwrap(),
903 ];
904 let name_servers: Vec<NameServerConfig> =
905 ResolverConfig::default().name_servers().to_owned();
906
907 let mut resolver_builder = Resolver::<R>::builder_with_config(
908 ResolverConfig::from_parts(Some(domain), search, name_servers),
909 handle,
910 );
911 resolver_builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4Only;
912 let resolver = resolver_builder.build();
913
914 let response = resolver
916 .lookup_ip("www")
917 .await
918 .expect("failed to run lookup");
919
920 assert_ne!(response.iter().count(), 0);
921 for address in response.iter() {
922 assert!(address.is_ipv4(), "should only be looking up IPv4");
923 }
924 }
925
926 pub(crate) async fn idna_test<R: ConnectionProvider>(handle: R) {
928 let resolver =
929 Resolver::<R>::builder_with_config(ResolverConfig::default(), handle).build();
930
931 let response = resolver
932 .lookup_ip("中国.icom.museum.")
933 .await
934 .expect("failed to run lookup");
935
936 assert!(response.iter().next().is_some());
939 }
940
941 pub(crate) async fn localhost_ipv4_test<R: ConnectionProvider>(handle: R) {
943 let mut resolver_builder =
944 Resolver::<R>::builder_with_config(ResolverConfig::default(), handle);
945 resolver_builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4thenIpv6;
946 let resolver = resolver_builder.build();
947
948 let response = resolver
949 .lookup_ip("localhost")
950 .await
951 .expect("failed to run lookup");
952
953 let mut iter = response.iter();
954 assert_eq!(iter.next().expect("no A"), IpAddr::V4(Ipv4Addr::LOCALHOST));
955 }
956
957 pub(crate) async fn localhost_ipv6_test<R: ConnectionProvider>(handle: R) {
959 let mut resolver_builder =
960 Resolver::<R>::builder_with_config(ResolverConfig::default(), handle);
961 resolver_builder.options_mut().ip_strategy = LookupIpStrategy::Ipv6thenIpv4;
962 let resolver = resolver_builder.build();
963
964 let response = resolver
965 .lookup_ip("localhost")
966 .await
967 .expect("failed to run lookup");
968
969 let mut iter = response.iter();
970 assert_eq!(
971 iter.next().expect("no AAAA"),
972 IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1,))
973 );
974 }
975
976 pub(crate) async fn search_ipv4_large_ndots_test<R: ConnectionProvider>(handle: R) {
978 let mut config = ResolverConfig::default();
979 config.add_search(Name::from_str("example.com").unwrap());
980
981 let mut resolver_builder = Resolver::<R>::builder_with_config(config, handle);
982 resolver_builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4Only;
983 resolver_builder.options_mut().ndots = 5;
984 let resolver = resolver_builder.build();
985
986 let response = resolver
987 .lookup_ip("198.51.100.35")
988 .await
989 .expect("failed to run lookup");
990
991 let mut iter = response.iter();
992 assert_eq!(
993 iter.next().expect("no rdatas"),
994 IpAddr::V4(Ipv4Addr::new(198, 51, 100, 35))
995 );
996 }
997
998 pub(crate) async fn search_ipv6_large_ndots_test<R: ConnectionProvider>(handle: R) {
1000 let mut config = ResolverConfig::default();
1001 config.add_search(Name::from_str("example.com").unwrap());
1002
1003 let mut resolver_builder = Resolver::<R>::builder_with_config(config, handle);
1004 resolver_builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4Only;
1005 resolver_builder.options_mut().ndots = 5;
1006 let resolver = resolver_builder.build();
1007
1008 let response = resolver
1009 .lookup_ip("2001:db8::c633:6423")
1010 .await
1011 .expect("failed to run lookup");
1012
1013 let mut iter = response.iter();
1014 assert_eq!(
1015 iter.next().expect("no rdatas"),
1016 IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc633, 0x6423))
1017 );
1018 }
1019
1020 pub(crate) async fn search_ipv6_name_parse_fails_test<R: ConnectionProvider>(handle: R) {
1022 let mut config = ResolverConfig::default();
1023 config.add_search(Name::from_str("example.com").unwrap());
1024
1025 let mut resolver_builder = Resolver::<R>::builder_with_config(config, handle);
1026 resolver_builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4Only;
1027 resolver_builder.options_mut().ndots = 5;
1028 let resolver = resolver_builder.build();
1029
1030 let response = resolver
1031 .lookup_ip("2001:db8::198.51.100.35")
1032 .await
1033 .expect("failed to run lookup");
1034
1035 let mut iter = response.iter();
1036 assert_eq!(
1037 iter.next().expect("no rdatas"),
1038 IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc633, 0x6423))
1039 );
1040 }
1041}
1042
1043#[cfg(test)]
1044#[cfg(feature = "tokio")]
1045#[allow(clippy::extra_unused_type_parameters)]
1046mod tests {
1047 use std::net::{IpAddr, Ipv4Addr};
1048 use std::sync::Mutex;
1049
1050 use futures_util::stream::once;
1051 use futures_util::{Stream, future};
1052 use test_support::subscribe;
1053 use tokio::runtime::Runtime;
1054
1055 use super::testing::{
1056 domain_search_test, fqdn_test, idna_test, ip_lookup_across_threads_test, ip_lookup_test,
1057 large_ndots_test, localhost_ipv4_test, localhost_ipv6_test, lookup_test, ndots_test,
1058 search_ipv4_large_ndots_test, search_ipv6_large_ndots_test,
1059 search_ipv6_name_parse_fails_test, search_list_test,
1060 };
1061 #[cfg(feature = "system-config")]
1062 use super::testing::{hosts_lookup_test, system_lookup_test};
1063 #[cfg(feature = "__dnssec")]
1064 use super::testing::{sec_lookup_fails_test, sec_lookup_test};
1065 use super::*;
1066 use crate::config::{ResolverConfig, ResolverOpts};
1067 use crate::name_server::GenericConnection;
1068 use crate::proto::op::Message;
1069 use crate::proto::rr::rdata::A;
1070 use crate::proto::xfer::{DnsRequest, DnsResponse};
1071 use crate::proto::{ProtoError, ProtoErrorKind};
1072
1073 fn is_send_t<T: Send>() -> bool {
1074 true
1075 }
1076
1077 fn is_sync_t<T: Sync>() -> bool {
1078 true
1079 }
1080
1081 #[test]
1082 fn test_send_sync() {
1083 assert!(is_send_t::<ResolverConfig>());
1084 assert!(is_sync_t::<ResolverConfig>());
1085 assert!(is_send_t::<ResolverOpts>());
1086 assert!(is_sync_t::<ResolverOpts>());
1087
1088 assert!(is_send_t::<Resolver<TokioConnectionProvider>>());
1089 assert!(is_sync_t::<Resolver<TokioConnectionProvider>>());
1090
1091 assert!(is_send_t::<DnsRequest>());
1092 assert!(is_send_t::<LookupIpFuture<GenericConnection>>());
1093 assert!(is_send_t::<LookupFuture<GenericConnection>>());
1094 }
1095
1096 #[tokio::test]
1097 async fn test_lookup_google() {
1098 subscribe();
1099 let handle = TokioConnectionProvider::default();
1100 lookup_test(ResolverConfig::google(), handle).await;
1101 }
1102
1103 #[tokio::test]
1104 async fn test_lookup_cloudflare() {
1105 subscribe();
1106 let handle = TokioConnectionProvider::default();
1107 lookup_test(ResolverConfig::cloudflare(), handle).await;
1108 }
1109
1110 #[tokio::test]
1111 async fn test_ip_lookup() {
1112 subscribe();
1113 let handle = TokioConnectionProvider::default();
1114 ip_lookup_test(handle).await;
1115 }
1116
1117 #[test]
1118 fn test_ip_lookup_across_threads() {
1119 subscribe();
1120 let handle = TokioConnectionProvider::default();
1121 ip_lookup_across_threads_test::<Runtime, _>(handle);
1122 }
1123
1124 #[tokio::test]
1125 #[cfg(feature = "__dnssec")]
1126 async fn test_sec_lookup() {
1127 subscribe();
1128 let handle = TokioConnectionProvider::default();
1129 sec_lookup_test(handle).await;
1130 }
1131
1132 #[tokio::test]
1133 #[cfg(feature = "__dnssec")]
1134 async fn test_sec_lookup_fails() {
1135 subscribe();
1136 let handle = TokioConnectionProvider::default();
1137 sec_lookup_fails_test(handle).await;
1138 }
1139
1140 #[tokio::test]
1141 #[ignore]
1142 #[cfg(any(unix, target_os = "windows"))]
1143 #[cfg(feature = "system-config")]
1144 async fn test_system_lookup() {
1145 subscribe();
1146 let handle = TokioConnectionProvider::default();
1147 system_lookup_test(handle).await;
1148 }
1149
1150 #[tokio::test]
1151 #[ignore]
1152 #[cfg(unix)]
1154 #[cfg(feature = "system-config")]
1155 async fn test_hosts_lookup() {
1156 subscribe();
1157 let handle = TokioConnectionProvider::default();
1158 hosts_lookup_test(handle).await;
1159 }
1160
1161 #[tokio::test]
1162 async fn test_fqdn() {
1163 subscribe();
1164 let handle = TokioConnectionProvider::default();
1165 fqdn_test(handle).await;
1166 }
1167
1168 #[tokio::test]
1169 async fn test_ndots() {
1170 subscribe();
1171 let handle = TokioConnectionProvider::default();
1172 ndots_test(handle).await;
1173 }
1174
1175 #[tokio::test]
1176 async fn test_large_ndots() {
1177 subscribe();
1178 let handle = TokioConnectionProvider::default();
1179 large_ndots_test(handle).await;
1180 }
1181
1182 #[tokio::test]
1183 async fn test_domain_search() {
1184 subscribe();
1185 let handle = TokioConnectionProvider::default();
1186 domain_search_test(handle).await;
1187 }
1188
1189 #[tokio::test]
1190 async fn test_search_list() {
1191 subscribe();
1192 let handle = TokioConnectionProvider::default();
1193 search_list_test(handle).await;
1194 }
1195
1196 #[tokio::test]
1197 async fn test_idna() {
1198 subscribe();
1199 let handle = TokioConnectionProvider::default();
1200 idna_test(handle).await;
1201 }
1202
1203 #[tokio::test]
1204 async fn test_localhost_ipv4() {
1205 subscribe();
1206 let handle = TokioConnectionProvider::default();
1207 localhost_ipv4_test(handle).await;
1208 }
1209
1210 #[tokio::test]
1211 async fn test_localhost_ipv6() {
1212 subscribe();
1213 let handle = TokioConnectionProvider::default();
1214 localhost_ipv6_test(handle).await;
1215 }
1216
1217 #[tokio::test]
1218 async fn test_search_ipv4_large_ndots() {
1219 subscribe();
1220 let handle = TokioConnectionProvider::default();
1221 search_ipv4_large_ndots_test(handle).await;
1222 }
1223
1224 #[tokio::test]
1225 async fn test_search_ipv6_large_ndots() {
1226 subscribe();
1227 let handle = TokioConnectionProvider::default();
1228 search_ipv6_large_ndots_test(handle).await;
1229 }
1230
1231 #[tokio::test]
1232 async fn test_search_ipv6_name_parse_fails() {
1233 subscribe();
1234 let handle = TokioConnectionProvider::default();
1235 search_ipv6_name_parse_fails_test(handle).await;
1236 }
1237
1238 #[test]
1239 fn test_build_names() {
1240 use std::str::FromStr;
1241
1242 let handle = TokioConnectionProvider::default();
1243 let mut config = ResolverConfig::default();
1244 config.add_search(Name::from_ascii("example.com.").unwrap());
1245 let resolver = Resolver::builder_with_config(config, handle).build();
1246
1247 assert_eq!(resolver.build_names(Name::from_str("").unwrap()).len(), 2);
1248 assert_eq!(resolver.build_names(Name::from_str(".").unwrap()).len(), 1);
1249
1250 let fqdn = Name::from_str("foo.example.com.").unwrap();
1251 let name_list = resolver.build_names(Name::from_str("foo").unwrap());
1252 assert!(name_list.contains(&fqdn));
1253
1254 let name_list = resolver.build_names(fqdn.clone());
1255 assert_eq!(name_list.len(), 1);
1256 assert_eq!(name_list.first(), Some(&fqdn));
1257 }
1258
1259 #[test]
1260 fn test_build_names_onion() {
1261 let handle = TokioConnectionProvider::default();
1262 let mut config = ResolverConfig::default();
1263 config.add_search(Name::from_ascii("example.com.").unwrap());
1264 let resolver = Resolver::builder_with_config(config, handle).build();
1265 let tor_address = [
1266 Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion")
1267 .unwrap(),
1268 Name::from_ascii("www.2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion")
1269 .unwrap(), ];
1271 let not_tor_address = [
1272 Name::from_ascii("onion").unwrap(),
1273 Name::from_ascii("www.onion").unwrap(),
1274 Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.www.onion")
1275 .unwrap(), Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion.to")
1277 .unwrap(), ];
1279 for name in &tor_address {
1280 assert_eq!(resolver.build_names(name.clone()).len(), 1);
1281 }
1282 for name in ¬_tor_address {
1283 assert_eq!(resolver.build_names(name.clone()).len(), 2);
1284 }
1285 }
1286
1287 #[tokio::test]
1288 async fn test_lookup() {
1289 assert_eq!(
1290 LookupFuture::lookup(
1291 vec![Name::root()],
1292 RecordType::A,
1293 DnsRequestOptions::default(),
1294 CachingClient::new(0, mock(vec![v4_message()]), false),
1295 )
1296 .await
1297 .unwrap()
1298 .iter()
1299 .map(|r| r.ip_addr().unwrap())
1300 .collect::<Vec<IpAddr>>(),
1301 vec![Ipv4Addr::LOCALHOST]
1302 );
1303 }
1304
1305 #[tokio::test]
1306 async fn test_lookup_slice() {
1307 assert_eq!(
1308 Record::data(
1309 &LookupFuture::lookup(
1310 vec![Name::root()],
1311 RecordType::A,
1312 DnsRequestOptions::default(),
1313 CachingClient::new(0, mock(vec![v4_message()]), false),
1314 )
1315 .await
1316 .unwrap()
1317 .records()[0]
1318 )
1319 .ip_addr()
1320 .unwrap(),
1321 Ipv4Addr::LOCALHOST
1322 );
1323 }
1324
1325 #[tokio::test]
1326 async fn test_lookup_into_iter() {
1327 assert_eq!(
1328 LookupFuture::lookup(
1329 vec![Name::root()],
1330 RecordType::A,
1331 DnsRequestOptions::default(),
1332 CachingClient::new(0, mock(vec![v4_message()]), false),
1333 )
1334 .await
1335 .unwrap()
1336 .into_iter()
1337 .map(|r| r.ip_addr().unwrap())
1338 .collect::<Vec<IpAddr>>(),
1339 vec![Ipv4Addr::LOCALHOST]
1340 );
1341 }
1342
1343 #[tokio::test]
1344 async fn test_error() {
1345 assert!(
1346 LookupFuture::lookup(
1347 vec![Name::root()],
1348 RecordType::A,
1349 DnsRequestOptions::default(),
1350 CachingClient::new(0, mock(vec![error()]), false),
1351 )
1352 .await
1353 .is_err()
1354 );
1355 }
1356
1357 #[tokio::test]
1358 async fn test_empty_no_response() {
1359 if let ProtoErrorKind::NoRecordsFound {
1360 query,
1361 negative_ttl,
1362 ..
1363 } = LookupFuture::lookup(
1364 vec![Name::root()],
1365 RecordType::A,
1366 DnsRequestOptions::default(),
1367 CachingClient::new(0, mock(vec![empty()]), false),
1368 )
1369 .await
1370 .expect_err("this should have been a NoRecordsFound")
1371 .proto()
1372 .expect("it should have been a ProtoError")
1373 .kind()
1374 {
1375 assert_eq!(**query, Query::query(Name::root(), RecordType::A));
1376 assert_eq!(*negative_ttl, None);
1377 } else {
1378 panic!("wrong error received");
1379 }
1380 }
1381
1382 #[derive(Clone)]
1383 struct MockDnsHandle {
1384 messages: Arc<Mutex<Vec<Result<DnsResponse, ProtoError>>>>,
1385 }
1386
1387 impl DnsHandle for MockDnsHandle {
1388 type Response = Pin<Box<dyn Stream<Item = Result<DnsResponse, ProtoError>> + Send>>;
1389
1390 fn send<R: Into<DnsRequest>>(&self, _: R) -> Self::Response {
1391 Box::pin(once(
1392 future::ready(self.messages.lock().unwrap().pop().unwrap_or_else(empty)).boxed(),
1393 ))
1394 }
1395 }
1396
1397 fn v4_message() -> Result<DnsResponse, ProtoError> {
1398 let mut message = Message::new();
1399 message.add_query(Query::query(Name::root(), RecordType::A));
1400 message.insert_answers(vec![Record::from_rdata(
1401 Name::root(),
1402 86400,
1403 RData::A(A::new(127, 0, 0, 1)),
1404 )]);
1405
1406 let resp = DnsResponse::from_message(message).unwrap();
1407 assert!(resp.contains_answer());
1408 Ok(resp)
1409 }
1410
1411 fn empty() -> Result<DnsResponse, ProtoError> {
1412 Ok(DnsResponse::from_message(Message::new()).unwrap())
1413 }
1414
1415 fn error() -> Result<DnsResponse, ProtoError> {
1416 Err(ProtoError::from(std::io::Error::from(
1417 std::io::ErrorKind::Other,
1418 )))
1419 }
1420
1421 fn mock(messages: Vec<Result<DnsResponse, ProtoError>>) -> MockDnsHandle {
1422 MockDnsHandle {
1423 messages: Arc::new(Mutex::new(messages)),
1424 }
1425 }
1426}