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