1use std::fmt;
10use std::net::IpAddr;
11use std::sync::Arc;
12
13use proto::error::ProtoResult;
14use proto::op::Query;
15use proto::rr::domain::usage::ONION;
16use proto::rr::domain::TryParseIp;
17use proto::rr::{IntoName, Name, Record, RecordType};
18use proto::xfer::{DnsRequestOptions, RetryDnsHandle};
19use tracing::{debug, trace};
20
21use crate::caching_client::CachingClient;
22use crate::config::{ResolverConfig, ResolverOpts};
23use crate::dns_lru::{self, DnsLru};
24use crate::error::*;
25use crate::lookup::{self, Lookup, LookupEither, LookupFuture};
26use crate::lookup_ip::{LookupIp, LookupIpFuture};
27#[cfg(feature = "tokio-runtime")]
28use crate::name_server::TokioConnectionProvider;
29use crate::name_server::{ConnectionProvider, NameServerPool};
30
31use crate::Hosts;
32
33#[derive(Clone)]
60pub struct AsyncResolver<P: ConnectionProvider> {
61 config: ResolverConfig,
62 options: ResolverOpts,
63 client_cache: CachingClient<LookupEither<P>, ResolveError>,
64 hosts: Option<Arc<Hosts>>,
65}
66
67#[cfg(feature = "tokio-runtime")]
69#[cfg_attr(docsrs, doc(cfg(feature = "tokio-runtime")))]
70pub type TokioAsyncResolver = AsyncResolver<TokioConnectionProvider>;
71
72macro_rules! lookup_fn {
73 ($p:ident, $l:ty, $r:path) => {
74 pub async fn $p<N: IntoName>(&self, query: N) -> Result<$l, ResolveError> {
82 let name = match query.into_name() {
83 Ok(name) => name,
84 Err(err) => {
85 return Err(err.into());
86 }
87 };
88
89 self.inner_lookup(name, $r, self.request_options()).await
90 }
91 };
92 ($p:ident, $l:ty, $r:path, $t:ty) => {
93 pub async fn $p(&self, query: $t) -> Result<$l, ResolveError> {
99 let name = Name::from(query);
100 self.inner_lookup(name, $r, self.request_options()).await
101 }
102 };
103}
104
105#[cfg(feature = "tokio-runtime")]
106#[cfg_attr(docsrs, doc(cfg(feature = "tokio-runtime")))]
107impl TokioAsyncResolver {
108 pub fn tokio(config: ResolverConfig, options: ResolverOpts) -> Self {
122 Self::new(config, options, TokioConnectionProvider::default())
123 }
124
125 #[cfg(any(unix, target_os = "windows"))]
129 #[cfg(feature = "system-config")]
130 #[cfg_attr(
131 docsrs,
132 doc(cfg(all(feature = "system-config", any(unix, target_os = "windows"))))
133 )]
134 pub fn tokio_from_system_conf() -> Result<Self, ResolveError> {
135 Self::from_system_conf(TokioConnectionProvider::default())
136 }
137}
138
139impl<R: ConnectionProvider> AsyncResolver<R> {
140 pub fn new(config: ResolverConfig, options: ResolverOpts, provider: R) -> Self {
156 Self::new_with_conn(config, options, provider)
157 }
158
159 #[cfg(any(unix, target_os = "windows"))]
165 #[cfg(feature = "system-config")]
166 #[cfg_attr(
167 docsrs,
168 doc(cfg(all(feature = "system-config", any(unix, target_os = "windows"))))
169 )]
170 pub fn from_system_conf(runtime: R) -> Result<Self, ResolveError> {
171 Self::from_system_conf_with_provider(runtime)
172 }
173
174 pub fn clear_cache(&self) {
176 self.client_cache.clear_cache();
177 }
178}
179
180impl<P: ConnectionProvider> AsyncResolver<P> {
181 pub fn new_with_conn(config: ResolverConfig, options: ResolverOpts, conn_provider: P) -> Self {
195 let pool =
196 NameServerPool::from_config_with_provider(&config, options.clone(), conn_provider);
197 let either;
198 let client = RetryDnsHandle::new(pool, options.attempts);
199 if options.validate {
200 #[cfg(feature = "dnssec")]
201 {
202 use proto::xfer::DnssecDnsHandle;
203 either = LookupEither::Secure(DnssecDnsHandle::new(client));
204 }
205
206 #[cfg(not(feature = "dnssec"))]
207 {
208 tracing::warn!("validate option is only available with 'dnssec' feature");
210 either = LookupEither::Retry(client);
211 }
212 } else {
213 either = LookupEither::Retry(client);
214 }
215
216 let hosts = if options.use_hosts_file {
217 Some(Arc::new(Hosts::new()))
218 } else {
219 None
220 };
221
222 trace!("handle passed back");
223 let lru = DnsLru::new(options.cache_size, dns_lru::TtlConfig::from_opts(&options));
224 Self {
225 config,
226 client_cache: CachingClient::with_cache(lru, either, options.preserve_intermediates),
227 options,
228 hosts,
229 }
230 }
231
232 #[cfg(any(unix, target_os = "windows"))]
236 #[cfg(feature = "system-config")]
237 #[cfg_attr(
238 docsrs,
239 doc(cfg(all(feature = "system-config", any(unix, target_os = "windows"))))
240 )]
241 pub fn from_system_conf_with_provider(conn_provider: P) -> Result<Self, ResolveError> {
242 let (config, options) = super::system_conf::read_system_conf()?;
243 Ok(Self::new_with_conn(config, options, conn_provider))
244 }
245
246 pub(crate) fn request_options(&self) -> DnsRequestOptions {
248 let mut request_opts = DnsRequestOptions::default();
249 request_opts.recursion_desired = self.options.recursion_desired;
250 request_opts.use_edns = self.options.edns0;
251
252 request_opts
253 }
254
255 pub async fn lookup<N: IntoName>(
268 &self,
269 name: N,
270 record_type: RecordType,
271 ) -> Result<Lookup, ResolveError> {
272 let name = match name.into_name() {
273 Ok(name) => name,
274 Err(err) => return Err(err.into()),
275 };
276
277 self.inner_lookup(name, record_type, self.request_options())
278 .await
279 }
280
281 fn push_name(name: Name, names: &mut Vec<Name>) {
282 if !names.contains(&name) {
283 names.push(name);
284 }
285 }
286
287 fn build_names(&self, name: Name) -> Vec<Name> {
288 if name.is_fqdn()
290 || ONION.zone_of(&name)
291 && name
292 .trim_to(2)
293 .iter()
294 .next()
295 .map(|name| name.len() == 56) .unwrap_or(false)
297 {
298 vec![name]
301 } else {
302 let mut names =
305 Vec::<Name>::with_capacity(1 + 1 + self.config.search().len());
306
307 let raw_name_first: bool =
309 name.num_labels() as usize > self.options.ndots || name.is_localhost();
310
311 if !raw_name_first {
313 names.push(name.clone());
314 }
315
316 for search in self.config.search().iter().rev() {
317 let name_search = name.clone().append_domain(search);
318
319 match name_search {
320 Ok(name_search) => Self::push_name(name_search, &mut names),
321 Err(e) => debug!(
322 "Not adding {} to {} for search due to error: {}",
323 search, name, e
324 ),
325 }
326 }
327
328 if let Some(domain) = self.config.domain() {
329 let name_search = name.clone().append_domain(domain);
330
331 match name_search {
332 Ok(name_search) => Self::push_name(name_search, &mut names),
333 Err(e) => debug!(
334 "Not adding {} to {} for search due to error: {}",
335 domain, name, e
336 ),
337 }
338 }
339
340 if raw_name_first {
342 names.push(name);
344 }
345
346 names
347 }
348 }
349
350 pub(crate) async fn inner_lookup<L>(
351 &self,
352 name: Name,
353 record_type: RecordType,
354 options: DnsRequestOptions,
355 ) -> Result<L, ResolveError>
356 where
357 L: From<Lookup> + Send + 'static,
358 {
359 let names = self.build_names(name);
360 LookupFuture::lookup(names, record_type, options, self.client_cache.clone())
361 .await
362 .map(L::from)
363 }
364
365 pub async fn lookup_ip<N: IntoName + TryParseIp>(
372 &self,
373 host: N,
374 ) -> Result<LookupIp, ResolveError> {
375 let mut finally_ip_addr: Option<Record> = None;
376 let maybe_ip = host.try_parse_ip();
377 let maybe_name: ProtoResult<Name> = host.into_name();
378
379 if let Some(ip_addr) = maybe_ip {
381 let name = maybe_name.clone().unwrap_or_default();
382 let record = Record::from_rdata(name.clone(), dns_lru::MAX_TTL, ip_addr.clone());
383
384 if self.options.ndots > 4 {
389 finally_ip_addr = Some(record);
390 } else {
391 let query = Query::query(name, ip_addr.record_type());
392 let lookup = Lookup::new_with_max_ttl(query, Arc::from([record]));
393 return Ok(lookup.into());
394 }
395 }
396
397 let name = match (maybe_name, finally_ip_addr.as_ref()) {
398 (Ok(name), _) => name,
399 (Err(_), Some(ip_addr)) => {
400 let query = Query::query(ip_addr.name().clone(), ip_addr.record_type());
402 let lookup = Lookup::new_with_max_ttl(query, Arc::from([ip_addr.clone()]));
403 return Ok(lookup.into());
404 }
405 (Err(err), None) => {
406 return Err(err.into());
407 }
408 };
409
410 let names = self.build_names(name);
411 let hosts = self.hosts.as_ref().cloned();
412
413 LookupIpFuture::lookup(
414 names,
415 self.options.ip_strategy,
416 self.client_cache.clone(),
417 self.request_options(),
418 hosts,
419 finally_ip_addr.and_then(Record::into_data),
420 )
421 .await
422 }
423
424 pub fn set_hosts(&mut self, hosts: Option<Hosts>) {
426 self.hosts = hosts.map(Arc::new);
427 }
428
429 lookup_fn!(
430 reverse_lookup,
431 lookup::ReverseLookup,
432 RecordType::PTR,
433 IpAddr
434 );
435 lookup_fn!(ipv4_lookup, lookup::Ipv4Lookup, RecordType::A);
436 lookup_fn!(ipv6_lookup, lookup::Ipv6Lookup, RecordType::AAAA);
437 lookup_fn!(mx_lookup, lookup::MxLookup, RecordType::MX);
438 lookup_fn!(ns_lookup, lookup::NsLookup, RecordType::NS);
439 lookup_fn!(soa_lookup, lookup::SoaLookup, RecordType::SOA);
440 lookup_fn!(srv_lookup, lookup::SrvLookup, RecordType::SRV);
441 lookup_fn!(tlsa_lookup, lookup::TlsaLookup, RecordType::TLSA);
442 lookup_fn!(txt_lookup, lookup::TxtLookup, RecordType::TXT);
443}
444
445impl<P: ConnectionProvider> fmt::Debug for AsyncResolver<P> {
446 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447 f.debug_struct("AsyncResolver")
448 .field("request_tx", &"...")
449 .finish()
450 }
451}
452
453#[cfg(any(test, feature = "testing"))]
455#[cfg_attr(docsrs, doc(cfg(feature = "testing")))]
456#[allow(dead_code, unreachable_pub)]
457pub mod testing {
458 use std::{net::*, str::FromStr};
459
460 use crate::config::{LookupIpStrategy, NameServerConfig, ResolverConfig, ResolverOpts};
461 use crate::name_server::ConnectionProvider;
462 use crate::AsyncResolver;
463 use proto::{rr::Name, Executor};
464
465 pub fn lookup_test<E: Executor, R: ConnectionProvider>(
467 config: ResolverConfig,
468 mut exec: E,
469 handle: R,
470 ) {
471 let resolver = AsyncResolver::<R>::new(config, ResolverOpts::default(), handle);
472
473 let response = exec
474 .block_on(resolver.lookup_ip("www.example.com."))
475 .expect("failed to run lookup");
476
477 assert_ne!(response.iter().count(), 0);
478 }
479
480 pub fn ip_lookup_test<E: Executor, R: ConnectionProvider>(mut exec: E, handle: R) {
482 let resolver =
483 AsyncResolver::<R>::new(ResolverConfig::default(), ResolverOpts::default(), handle);
484
485 let response = exec
486 .block_on(resolver.lookup_ip("10.1.0.2"))
487 .expect("failed to run lookup");
488
489 assert_eq!(
490 Some(IpAddr::V4(Ipv4Addr::new(10, 1, 0, 2))),
491 response.iter().next()
492 );
493
494 let response = exec
495 .block_on(resolver.lookup_ip("2606:2800:21f:cb07:6820:80da:af6b:8b2c"))
496 .expect("failed to run lookup");
497
498 assert_eq!(
499 Some(IpAddr::V6(Ipv6Addr::new(
500 0x2606, 0x2800, 0x21f, 0xcb07, 0x6820, 0x80da, 0xaf6b, 0x8b2c,
501 ))),
502 response.iter().next()
503 );
504 }
505
506 pub fn ip_lookup_across_threads_test<E: Executor + Send + 'static, R: ConnectionProvider>(
508 handle: R,
509 ) {
510 use std::thread;
514 let resolver =
515 AsyncResolver::<R>::new(ResolverConfig::default(), ResolverOpts::default(), handle);
516
517 let resolver_one = resolver.clone();
518 let resolver_two = resolver;
519
520 let test_fn = |resolver: AsyncResolver<R>| {
521 let mut exec = E::new();
522
523 let response = exec
524 .block_on(resolver.lookup_ip("10.1.0.2"))
525 .expect("failed to run lookup");
526
527 assert_eq!(
528 Some(IpAddr::V4(Ipv4Addr::new(10, 1, 0, 2))),
529 response.iter().next()
530 );
531
532 let response = exec
533 .block_on(resolver.lookup_ip("2606:2800:21f:cb07:6820:80da:af6b:8b2c"))
534 .expect("failed to run lookup");
535
536 assert_eq!(
537 Some(IpAddr::V6(Ipv6Addr::new(
538 0x2606, 0x2800, 0x21f, 0xcb07, 0x6820, 0x80da, 0xaf6b, 0x8b2c,
539 ))),
540 response.iter().next()
541 );
542 };
543
544 let thread_one = thread::spawn(move || {
545 test_fn(resolver_one);
546 });
547
548 let thread_two = thread::spawn(move || {
549 test_fn(resolver_two);
550 });
551
552 thread_one.join().expect("thread_one failed");
553 thread_two.join().expect("thread_two failed");
554 }
555
556 #[cfg(feature = "dnssec")]
558 #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
559 pub fn sec_lookup_test<E: Executor + Send + 'static, R: ConnectionProvider>(
560 mut exec: E,
561 handle: R,
562 ) {
563 let resolver = AsyncResolver::new(
566 ResolverConfig::default(),
567 ResolverOpts {
568 validate: true,
569 try_tcp_on_error: true,
570 ..ResolverOpts::default()
571 },
572 handle,
573 );
574
575 let response = exec
576 .block_on(resolver.lookup_ip("www.internetsociety.org."))
577 .expect("failed to run lookup");
578
579 assert_ne!(response.iter().count(), 0);
580 }
581
582 #[allow(deprecated)]
584 #[cfg(feature = "dnssec")]
585 #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
586 pub fn sec_lookup_fails_test<E: Executor + Send + 'static, R: ConnectionProvider>(
587 mut exec: E,
588 handle: R,
589 ) {
590 use crate::error::*;
591 use proto::rr::RecordType;
592 let resolver = AsyncResolver::new(
593 ResolverConfig::default(),
594 ResolverOpts {
595 validate: true,
596 ip_strategy: LookupIpStrategy::Ipv4Only,
597 ..ResolverOpts::default()
598 },
599 handle,
600 );
601
602 let response = exec.block_on(resolver.lookup_ip("hickory-dns.org."));
604
605 assert!(response.is_err());
606 let error = response.unwrap_err();
607
608 use proto::error::{ProtoError, ProtoErrorKind};
609
610 let error_str = format!("{error}");
611 let name = Name::from_str("hickory-dns.org.").unwrap();
612 let expected_str = format!(
613 "{}",
614 ResolveError::from(ProtoError::from(ProtoErrorKind::RrsigsNotPresent {
615 name,
616 record_type: RecordType::A
617 }))
618 );
619 assert_eq!(error_str, expected_str);
620 if let ResolveErrorKind::Proto(_) = *error.kind() {
621 } else {
622 panic!("wrong error")
623 }
624 }
625
626 #[cfg(feature = "system-config")]
628 #[cfg_attr(docsrs, doc(cfg(feature = "system-config")))]
629 pub fn system_lookup_test<E: Executor + Send + 'static, R: ConnectionProvider>(
630 mut exec: E,
631 handle: R,
632 ) {
633 let resolver =
634 AsyncResolver::<R>::from_system_conf(handle).expect("failed to create resolver");
635
636 let response = exec
637 .block_on(resolver.lookup_ip("www.example.com."))
638 .expect("failed to run lookup");
639
640 assert_ne!(response.iter().count(), 0);
641 }
642
643 #[cfg(feature = "system-config")]
645 #[cfg_attr(docsrs, doc(cfg(feature = "system-config")))]
646 pub fn hosts_lookup_test<E: Executor + Send + 'static, R: ConnectionProvider>(
647 mut exec: E,
648 handle: R,
649 ) {
650 let resolver =
651 AsyncResolver::<R>::from_system_conf(handle).expect("failed to create resolver");
652
653 let response = exec
654 .block_on(resolver.lookup_ip("a.com"))
655 .expect("failed to run lookup");
656
657 assert_eq!(response.iter().count(), 1);
658 for address in response.iter() {
659 if address.is_ipv4() {
660 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(10, 1, 0, 104)));
661 } else {
662 panic!("failed to run lookup");
663 }
664 }
665 }
666
667 pub fn fqdn_test<E: Executor + Send + 'static, R: ConnectionProvider>(mut exec: E, handle: R) {
669 let domain = Name::from_str("incorrect.example.com.").unwrap();
670 let search = vec![
671 Name::from_str("bad.example.com.").unwrap(),
672 Name::from_str("wrong.example.com.").unwrap(),
673 ];
674 let name_servers: Vec<NameServerConfig> =
675 ResolverConfig::default().name_servers().to_owned();
676
677 let resolver = AsyncResolver::<R>::new(
678 ResolverConfig::from_parts(Some(domain), search, name_servers),
679 ResolverOpts {
680 ip_strategy: LookupIpStrategy::Ipv4Only,
681 ..ResolverOpts::default()
682 },
683 handle,
684 );
685
686 let response = exec
687 .block_on(resolver.lookup_ip("www.example.com."))
688 .expect("failed to run lookup");
689
690 assert_ne!(response.iter().count(), 0);
691 }
692
693 pub fn ndots_test<E: Executor + Send + 'static, R: ConnectionProvider>(mut exec: E, handle: R) {
695 let domain = Name::from_str("incorrect.example.com.").unwrap();
696 let search = vec![
697 Name::from_str("bad.example.com.").unwrap(),
698 Name::from_str("wrong.example.com.").unwrap(),
699 ];
700 let name_servers: Vec<NameServerConfig> =
701 ResolverConfig::default().name_servers().to_owned();
702
703 let resolver = AsyncResolver::<R>::new(
704 ResolverConfig::from_parts(Some(domain), search, name_servers),
705 ResolverOpts {
706 ndots: 2,
708 ip_strategy: LookupIpStrategy::Ipv4Only,
709 ..ResolverOpts::default()
710 },
711 handle,
712 );
713
714 let response = exec
716 .block_on(resolver.lookup_ip("www.example.com"))
717 .expect("failed to run lookup");
718
719 assert_ne!(response.iter().count(), 0);
720 }
721
722 pub fn large_ndots_test<E: Executor + Send + 'static, R: ConnectionProvider>(
724 mut exec: E,
725 handle: R,
726 ) {
727 let domain = Name::from_str("incorrect.example.com.").unwrap();
728 let search = vec![
729 Name::from_str("bad.example.com.").unwrap(),
730 Name::from_str("wrong.example.com.").unwrap(),
731 ];
732 let name_servers: Vec<NameServerConfig> =
733 ResolverConfig::default().name_servers().to_owned();
734
735 let resolver = AsyncResolver::<R>::new(
736 ResolverConfig::from_parts(Some(domain), search, name_servers),
737 ResolverOpts {
738 ndots: 5,
740 ip_strategy: LookupIpStrategy::Ipv4Only,
741 ..ResolverOpts::default()
742 },
743 handle,
744 );
745
746 let response = exec
748 .block_on(resolver.lookup_ip("www.example.com"))
749 .expect("failed to run lookup");
750
751 assert_ne!(response.iter().count(), 0);
752 }
753
754 pub fn domain_search_test<E: Executor + Send + 'static, R: ConnectionProvider>(
756 mut exec: E,
757 handle: R,
758 ) {
759 let domain = Name::from_str("example.com.").unwrap();
763 let search = vec![
764 Name::from_str("bad.example.com.").unwrap(),
765 Name::from_str("wrong.example.com.").unwrap(),
766 ];
767 let name_servers: Vec<NameServerConfig> =
768 ResolverConfig::default().name_servers().to_owned();
769
770 let resolver = AsyncResolver::<R>::new(
771 ResolverConfig::from_parts(Some(domain), search, name_servers),
772 ResolverOpts {
773 ip_strategy: LookupIpStrategy::Ipv4Only,
774 ..ResolverOpts::default()
775 },
776 handle,
777 );
778
779 let response = exec
781 .block_on(resolver.lookup_ip("www"))
782 .expect("failed to run lookup");
783
784 assert_ne!(response.iter().count(), 0);
785 }
786
787 pub fn search_list_test<E: Executor + Send + 'static, R: ConnectionProvider>(
789 mut exec: E,
790 handle: R,
791 ) {
792 let domain = Name::from_str("incorrect.example.com.").unwrap();
793 let search = vec![
794 Name::from_str("bad.example.com.").unwrap(),
796 Name::from_str("example.com.").unwrap(),
798 ];
799 let name_servers: Vec<NameServerConfig> =
800 ResolverConfig::default().name_servers().to_owned();
801
802 let resolver = AsyncResolver::<R>::new(
803 ResolverConfig::from_parts(Some(domain), search, name_servers),
804 ResolverOpts {
805 ip_strategy: LookupIpStrategy::Ipv4Only,
806 ..ResolverOpts::default()
807 },
808 handle,
809 );
810
811 let response = exec
813 .block_on(resolver.lookup_ip("www"))
814 .expect("failed to run lookup");
815
816 assert_ne!(response.iter().count(), 0);
817 }
818
819 pub fn idna_test<E: Executor + Send + 'static, R: ConnectionProvider>(mut exec: E, handle: R) {
821 let resolver =
822 AsyncResolver::<R>::new(ResolverConfig::default(), ResolverOpts::default(), handle);
823
824 let response = exec
825 .block_on(resolver.lookup_ip("中国.icom.museum."))
826 .expect("failed to run lookup");
827
828 assert!(response.iter().next().is_some());
831 }
832
833 pub fn localhost_ipv4_test<E: Executor + Send + 'static, R: ConnectionProvider>(
835 mut exec: E,
836 handle: R,
837 ) {
838 let resolver = AsyncResolver::<R>::new(
839 ResolverConfig::default(),
840 ResolverOpts {
841 ip_strategy: LookupIpStrategy::Ipv4thenIpv6,
842 ..ResolverOpts::default()
843 },
844 handle,
845 );
846
847 let response = exec
848 .block_on(resolver.lookup_ip("localhost"))
849 .expect("failed to run lookup");
850
851 let mut iter = response.iter();
852 assert_eq!(
853 iter.next().expect("no A"),
854 IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
855 );
856 }
857
858 pub fn localhost_ipv6_test<E: Executor + Send + 'static, R: ConnectionProvider>(
860 mut exec: E,
861 handle: R,
862 ) {
863 let resolver = AsyncResolver::<R>::new(
864 ResolverConfig::default(),
865 ResolverOpts {
866 ip_strategy: LookupIpStrategy::Ipv6thenIpv4,
867 ..ResolverOpts::default()
868 },
869 handle,
870 );
871
872 let response = exec
873 .block_on(resolver.lookup_ip("localhost"))
874 .expect("failed to run lookup");
875
876 let mut iter = response.iter();
877 assert_eq!(
878 iter.next().expect("no AAAA"),
879 IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1,))
880 );
881 }
882
883 pub fn search_ipv4_large_ndots_test<E: Executor + Send + 'static, R: ConnectionProvider>(
885 mut exec: E,
886 handle: R,
887 ) {
888 let mut config = ResolverConfig::default();
889 config.add_search(Name::from_str("example.com").unwrap());
890
891 let resolver = AsyncResolver::<R>::new(
892 config,
893 ResolverOpts {
894 ip_strategy: LookupIpStrategy::Ipv4Only,
895 ndots: 5,
896 ..ResolverOpts::default()
897 },
898 handle,
899 );
900
901 let response = exec
902 .block_on(resolver.lookup_ip("198.51.100.35"))
903 .expect("failed to run lookup");
904
905 let mut iter = response.iter();
906 assert_eq!(
907 iter.next().expect("no rdatas"),
908 IpAddr::V4(Ipv4Addr::new(198, 51, 100, 35))
909 );
910 }
911
912 pub fn search_ipv6_large_ndots_test<E: Executor + Send + 'static, R: ConnectionProvider>(
914 mut exec: E,
915 handle: R,
916 ) {
917 let mut config = ResolverConfig::default();
918 config.add_search(Name::from_str("example.com").unwrap());
919
920 let resolver = AsyncResolver::<R>::new(
921 config,
922 ResolverOpts {
923 ip_strategy: LookupIpStrategy::Ipv4Only,
924 ndots: 5,
925 ..ResolverOpts::default()
926 },
927 handle,
928 );
929
930 let response = exec
931 .block_on(resolver.lookup_ip("2001:db8::c633:6423"))
932 .expect("failed to run lookup");
933
934 let mut iter = response.iter();
935 assert_eq!(
936 iter.next().expect("no rdatas"),
937 IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc633, 0x6423))
938 );
939 }
940
941 pub fn search_ipv6_name_parse_fails_test<
943 E: Executor + Send + 'static,
944 R: ConnectionProvider,
945 >(
946 mut exec: E,
947 handle: R,
948 ) {
949 let mut config = ResolverConfig::default();
950 config.add_search(Name::from_str("example.com").unwrap());
951
952 let resolver = AsyncResolver::<R>::new(
953 config,
954 ResolverOpts {
955 ip_strategy: LookupIpStrategy::Ipv4Only,
956 ndots: 5,
957 ..ResolverOpts::default()
958 },
959 handle,
960 );
961
962 let response = exec
963 .block_on(resolver.lookup_ip("2001:db8::198.51.100.35"))
964 .expect("failed to run lookup");
965
966 let mut iter = response.iter();
967 assert_eq!(
968 iter.next().expect("no rdatas"),
969 IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc633, 0x6423))
970 );
971 }
972}
973#[cfg(test)]
974#[cfg(feature = "tokio-runtime")]
975#[allow(clippy::extra_unused_type_parameters)]
976mod tests {
977 use proto::xfer::DnsRequest;
978 use tokio::runtime::Runtime;
979
980 use crate::config::{ResolverConfig, ResolverOpts};
981 use crate::name_server::GenericConnection;
982
983 use super::*;
984
985 fn is_send_t<T: Send>() -> bool {
986 true
987 }
988
989 fn is_sync_t<T: Sync>() -> bool {
990 true
991 }
992
993 #[test]
994 fn test_send_sync() {
995 assert!(is_send_t::<ResolverConfig>());
996 assert!(is_sync_t::<ResolverConfig>());
997 assert!(is_send_t::<ResolverOpts>());
998 assert!(is_sync_t::<ResolverOpts>());
999
1000 assert!(is_send_t::<AsyncResolver<TokioConnectionProvider>>());
1001 assert!(is_sync_t::<AsyncResolver<TokioConnectionProvider>>());
1002
1003 assert!(is_send_t::<DnsRequest>());
1004 assert!(is_send_t::<LookupIpFuture<GenericConnection, ResolveError>>());
1005 assert!(is_send_t::<LookupFuture<GenericConnection, ResolveError>>());
1006 }
1007
1008 #[test]
1009 fn test_lookup_google() {
1010 use super::testing::lookup_test;
1011 let io_loop = Runtime::new().expect("failed to create tokio runtime");
1012 let handle = TokioConnectionProvider::default();
1013 lookup_test::<Runtime, TokioConnectionProvider>(ResolverConfig::google(), io_loop, handle)
1014 }
1015
1016 #[test]
1017 fn test_lookup_cloudflare() {
1018 use super::testing::lookup_test;
1019 let io_loop = Runtime::new().expect("failed to create tokio runtime");
1020 let handle = TokioConnectionProvider::default();
1021 lookup_test::<Runtime, TokioConnectionProvider>(
1022 ResolverConfig::cloudflare(),
1023 io_loop,
1024 handle,
1025 )
1026 }
1027
1028 #[test]
1029 fn test_lookup_quad9() {
1030 use super::testing::lookup_test;
1031 let io_loop = Runtime::new().expect("failed to create tokio runtime");
1032 let handle = TokioConnectionProvider::default();
1033 lookup_test::<Runtime, TokioConnectionProvider>(ResolverConfig::quad9(), io_loop, handle)
1034 }
1035
1036 #[test]
1037 fn test_ip_lookup() {
1038 use super::testing::ip_lookup_test;
1039 let io_loop = Runtime::new().expect("failed to create tokio runtime");
1040 let handle = TokioConnectionProvider::default();
1041 ip_lookup_test::<Runtime, TokioConnectionProvider>(io_loop, handle)
1042 }
1043
1044 #[test]
1045 fn test_ip_lookup_across_threads() {
1046 use super::testing::ip_lookup_across_threads_test;
1047 let _io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1048 let handle = TokioConnectionProvider::default();
1049 ip_lookup_across_threads_test::<Runtime, TokioConnectionProvider>(handle)
1050 }
1051
1052 #[test]
1053 #[cfg(feature = "dnssec")]
1054 fn test_sec_lookup() {
1055 use super::testing::sec_lookup_test;
1056 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1057 let handle = TokioConnectionProvider::default();
1058 sec_lookup_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1059 }
1060
1061 #[test]
1062 #[cfg(feature = "dnssec")]
1063 fn test_sec_lookup_fails() {
1064 use super::testing::sec_lookup_fails_test;
1065 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1066 let handle = TokioConnectionProvider::default();
1067 sec_lookup_fails_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1068 }
1069
1070 #[test]
1071 #[ignore]
1072 #[cfg(any(unix, target_os = "windows"))]
1073 #[cfg(feature = "system-config")]
1074 fn test_system_lookup() {
1075 use super::testing::system_lookup_test;
1076 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1077 let handle = TokioConnectionProvider::default();
1078 system_lookup_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1079 }
1080
1081 #[test]
1082 #[ignore]
1083 #[cfg(unix)]
1085 #[cfg(feature = "system-config")]
1086 fn test_hosts_lookup() {
1087 use super::testing::hosts_lookup_test;
1088 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1089 let handle = TokioConnectionProvider::default();
1090 hosts_lookup_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1091 }
1092
1093 #[test]
1094 fn test_fqdn() {
1095 use super::testing::fqdn_test;
1096 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1097 let handle = TokioConnectionProvider::default();
1098 fqdn_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1099 }
1100
1101 #[test]
1102 fn test_ndots() {
1103 use super::testing::ndots_test;
1104 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1105 let handle = TokioConnectionProvider::default();
1106 ndots_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1107 }
1108
1109 #[test]
1110 fn test_large_ndots() {
1111 use super::testing::large_ndots_test;
1112 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1113 let handle = TokioConnectionProvider::default();
1114 large_ndots_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1115 }
1116
1117 #[test]
1118 fn test_domain_search() {
1119 use super::testing::domain_search_test;
1120 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1121 let handle = TokioConnectionProvider::default();
1122 domain_search_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1123 }
1124
1125 #[test]
1126 fn test_search_list() {
1127 use super::testing::search_list_test;
1128 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1129 let handle = TokioConnectionProvider::default();
1130 search_list_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1131 }
1132
1133 #[test]
1134 fn test_idna() {
1135 use super::testing::idna_test;
1136 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1137 let handle = TokioConnectionProvider::default();
1138 idna_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1139 }
1140
1141 #[test]
1142 fn test_localhost_ipv4() {
1143 use super::testing::localhost_ipv4_test;
1144 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1145 let handle = TokioConnectionProvider::default();
1146 localhost_ipv4_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1147 }
1148
1149 #[test]
1150 fn test_localhost_ipv6() {
1151 use super::testing::localhost_ipv6_test;
1152 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1153 let handle = TokioConnectionProvider::default();
1154 localhost_ipv6_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1155 }
1156
1157 #[test]
1158 fn test_search_ipv4_large_ndots() {
1159 use super::testing::search_ipv4_large_ndots_test;
1160 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1161 let handle = TokioConnectionProvider::default();
1162 search_ipv4_large_ndots_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1163 }
1164
1165 #[test]
1166 fn test_search_ipv6_large_ndots() {
1167 use super::testing::search_ipv6_large_ndots_test;
1168 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1169 let handle = TokioConnectionProvider::default();
1170 search_ipv6_large_ndots_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1171 }
1172
1173 #[test]
1174 fn test_search_ipv6_name_parse_fails() {
1175 use super::testing::search_ipv6_name_parse_fails_test;
1176 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1177 let handle = TokioConnectionProvider::default();
1178 search_ipv6_name_parse_fails_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1179 }
1180
1181 #[test]
1182 fn test_build_names_onion() {
1183 let handle = TokioConnectionProvider::default();
1184 let mut config = ResolverConfig::default();
1185 config.add_search(Name::from_ascii("example.com.").unwrap());
1186 let resolver =
1187 AsyncResolver::<TokioConnectionProvider>::new(config, ResolverOpts::default(), handle);
1188 let tor_address = [
1189 Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion")
1190 .unwrap(),
1191 Name::from_ascii("www.2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion")
1192 .unwrap(), ];
1194 let not_tor_address = [
1195 Name::from_ascii("onion").unwrap(),
1196 Name::from_ascii("www.onion").unwrap(),
1197 Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.www.onion")
1198 .unwrap(), Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion.to")
1200 .unwrap(), ];
1202 for name in &tor_address {
1203 assert_eq!(resolver.build_names(name.clone()).len(), 1);
1204 }
1205 for name in ¬_tor_address {
1206 assert_eq!(resolver.build_names(name.clone()).len(), 2);
1207 }
1208 }
1209}