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 = NameServerPool::from_config_with_provider(&config, &options, conn_provider);
196 let either;
197 let client = RetryDnsHandle::new(pool, options.attempts);
198 if options.validate {
199 #[cfg(feature = "dnssec")]
200 {
201 use proto::xfer::DnssecDnsHandle;
202 either = LookupEither::Secure(DnssecDnsHandle::new(client));
203 }
204
205 #[cfg(not(feature = "dnssec"))]
206 {
207 tracing::warn!("validate option is only available with 'dnssec' feature");
209 either = LookupEither::Retry(client);
210 }
211 } else {
212 either = LookupEither::Retry(client);
213 }
214
215 let hosts = if options.use_hosts_file {
216 Some(Arc::new(Hosts::new()))
217 } else {
218 None
219 };
220
221 trace!("handle passed back");
222 let lru = DnsLru::new(options.cache_size, dns_lru::TtlConfig::from_opts(&options));
223 Self {
224 config,
225 options,
226 client_cache: CachingClient::with_cache(lru, either, options.preserve_intermediates),
227 hosts,
228 }
229 }
230
231 #[cfg(any(unix, target_os = "windows"))]
235 #[cfg(feature = "system-config")]
236 #[cfg_attr(
237 docsrs,
238 doc(cfg(all(feature = "system-config", any(unix, target_os = "windows"))))
239 )]
240 pub fn from_system_conf_with_provider(conn_provider: P) -> Result<Self, ResolveError> {
241 let (config, options) = super::system_conf::read_system_conf()?;
242 Ok(Self::new_with_conn(config, options, conn_provider))
243 }
244
245 pub(crate) fn request_options(&self) -> DnsRequestOptions {
247 let mut request_opts = DnsRequestOptions::default();
248 request_opts.recursion_desired = self.options.recursion_desired;
249 request_opts.use_edns = self.options.edns0;
250
251 request_opts
252 }
253
254 pub async fn lookup<N: IntoName>(
267 &self,
268 name: N,
269 record_type: RecordType,
270 ) -> Result<Lookup, ResolveError> {
271 let name = match name.into_name() {
272 Ok(name) => name,
273 Err(err) => return Err(err.into()),
274 };
275
276 self.inner_lookup(name, record_type, self.request_options())
277 .await
278 }
279
280 fn push_name(name: Name, names: &mut Vec<Name>) {
281 if !names.contains(&name) {
282 names.push(name);
283 }
284 }
285
286 fn build_names(&self, name: Name) -> Vec<Name> {
287 if name.is_fqdn()
289 || ONION.zone_of(&name)
290 && name
291 .trim_to(2)
292 .iter()
293 .next()
294 .map(|name| name.len() == 56) .unwrap_or(false)
296 {
297 vec![name]
300 } else {
301 let mut names =
304 Vec::<Name>::with_capacity(1 + 1 + self.config.search().len());
305
306 let raw_name_first: bool =
308 name.num_labels() as usize > self.options.ndots || name.is_localhost();
309
310 if !raw_name_first {
312 names.push(name.clone());
313 }
314
315 for search in self.config.search().iter().rev() {
316 let name_search = name.clone().append_domain(search);
317
318 match name_search {
319 Ok(name_search) => Self::push_name(name_search, &mut names),
320 Err(e) => debug!(
321 "Not adding {} to {} for search due to error: {}",
322 search, name, e
323 ),
324 }
325 }
326
327 if let Some(domain) = self.config.domain() {
328 let name_search = name.clone().append_domain(domain);
329
330 match name_search {
331 Ok(name_search) => Self::push_name(name_search, &mut names),
332 Err(e) => debug!(
333 "Not adding {} to {} for search due to error: {}",
334 domain, name, e
335 ),
336 }
337 }
338
339 if raw_name_first {
341 names.push(name);
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 + 'static,
357 {
358 let names = self.build_names(name);
359 LookupFuture::lookup(names, record_type, options, self.client_cache.clone())
360 .await
361 .map(L::from)
362 }
363
364 pub async fn lookup_ip<N: IntoName + TryParseIp>(
371 &self,
372 host: N,
373 ) -> Result<LookupIp, ResolveError> {
374 let mut finally_ip_addr: Option<Record> = None;
375 let maybe_ip = host.try_parse_ip();
376 let maybe_name: ProtoResult<Name> = host.into_name();
377
378 if let Some(ip_addr) = maybe_ip {
380 let name = maybe_name.clone().unwrap_or_default();
381 let record = Record::from_rdata(name.clone(), dns_lru::MAX_TTL, ip_addr.clone());
382
383 if self.options.ndots > 4 {
388 finally_ip_addr = Some(record);
389 } else {
390 let query = Query::query(name, ip_addr.record_type());
391 let lookup = Lookup::new_with_max_ttl(query, Arc::from([record]));
392 return Ok(lookup.into());
393 }
394 }
395
396 let name = match (maybe_name, finally_ip_addr.as_ref()) {
397 (Ok(name), _) => name,
398 (Err(_), Some(ip_addr)) => {
399 let query = Query::query(ip_addr.name().clone(), ip_addr.record_type());
401 let lookup = Lookup::new_with_max_ttl(query, Arc::from([ip_addr.clone()]));
402 return Ok(lookup.into());
403 }
404 (Err(err), None) => {
405 return Err(err.into());
406 }
407 };
408
409 let names = self.build_names(name);
410 let hosts = self.hosts.as_ref().cloned();
411
412 LookupIpFuture::lookup(
413 names,
414 self.options.ip_strategy,
415 self.client_cache.clone(),
416 self.request_options(),
417 hosts,
418 finally_ip_addr.and_then(Record::into_data),
419 )
420 .await
421 }
422
423 pub fn set_hosts(&mut self, hosts: Option<Hosts>) {
425 self.hosts = hosts.map(Arc::new);
426 }
427
428 lookup_fn!(
429 reverse_lookup,
430 lookup::ReverseLookup,
431 RecordType::PTR,
432 IpAddr
433 );
434 lookup_fn!(ipv4_lookup, lookup::Ipv4Lookup, RecordType::A);
435 lookup_fn!(ipv6_lookup, lookup::Ipv6Lookup, RecordType::AAAA);
436 lookup_fn!(mx_lookup, lookup::MxLookup, RecordType::MX);
437 lookup_fn!(ns_lookup, lookup::NsLookup, RecordType::NS);
438 lookup_fn!(soa_lookup, lookup::SoaLookup, RecordType::SOA);
439 lookup_fn!(srv_lookup, lookup::SrvLookup, RecordType::SRV);
440 lookup_fn!(tlsa_lookup, lookup::TlsaLookup, RecordType::TLSA);
441 lookup_fn!(txt_lookup, lookup::TxtLookup, RecordType::TXT);
442}
443
444impl<P: ConnectionProvider> fmt::Debug for AsyncResolver<P> {
445 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446 f.debug_struct("AsyncResolver")
447 .field("request_tx", &"...")
448 .finish()
449 }
450}
451
452#[cfg(any(test, feature = "testing"))]
454#[cfg_attr(docsrs, doc(cfg(feature = "testing")))]
455#[allow(dead_code, unreachable_pub)]
456pub mod testing {
457 use std::{net::*, str::FromStr};
458
459 use crate::config::{LookupIpStrategy, NameServerConfig, ResolverConfig, ResolverOpts};
460 use crate::name_server::ConnectionProvider;
461 use crate::AsyncResolver;
462 use proto::{rr::Name, Executor};
463
464 pub fn lookup_test<E: Executor, R: ConnectionProvider>(
466 config: ResolverConfig,
467 mut exec: E,
468 handle: R,
469 ) {
470 let resolver = AsyncResolver::<R>::new(config, ResolverOpts::default(), handle);
471
472 let response = exec
473 .block_on(resolver.lookup_ip("www.example.com."))
474 .expect("failed to run lookup");
475
476 assert_eq!(response.iter().count(), 1);
477 for address in response.iter() {
478 if address.is_ipv4() {
479 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
480 } else {
481 assert_eq!(
482 address,
483 IpAddr::V6(Ipv6Addr::new(
484 0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
485 ))
486 );
487 }
488 }
489 }
490
491 pub fn ip_lookup_test<E: Executor, R: ConnectionProvider>(mut exec: E, handle: R) {
493 let resolver =
494 AsyncResolver::<R>::new(ResolverConfig::default(), ResolverOpts::default(), handle);
495
496 let response = exec
497 .block_on(resolver.lookup_ip("10.1.0.2"))
498 .expect("failed to run lookup");
499
500 assert_eq!(
501 Some(IpAddr::V4(Ipv4Addr::new(10, 1, 0, 2))),
502 response.iter().next()
503 );
504
505 let response = exec
506 .block_on(resolver.lookup_ip("2606:2800:220:1:248:1893:25c8:1946"))
507 .expect("failed to run lookup");
508
509 assert_eq!(
510 Some(IpAddr::V6(Ipv6Addr::new(
511 0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
512 ))),
513 response.iter().next()
514 );
515 }
516
517 pub fn ip_lookup_across_threads_test<E: Executor + Send + 'static, R: ConnectionProvider>(
519 handle: R,
520 ) {
521 use std::thread;
525 let resolver =
526 AsyncResolver::<R>::new(ResolverConfig::default(), ResolverOpts::default(), handle);
527
528 let resolver_one = resolver.clone();
529 let resolver_two = resolver;
530
531 let test_fn = |resolver: AsyncResolver<R>| {
532 let mut exec = E::new();
533
534 let response = exec
535 .block_on(resolver.lookup_ip("10.1.0.2"))
536 .expect("failed to run lookup");
537
538 assert_eq!(
539 Some(IpAddr::V4(Ipv4Addr::new(10, 1, 0, 2))),
540 response.iter().next()
541 );
542
543 let response = exec
544 .block_on(resolver.lookup_ip("2606:2800:220:1:248:1893:25c8:1946"))
545 .expect("failed to run lookup");
546
547 assert_eq!(
548 Some(IpAddr::V6(Ipv6Addr::new(
549 0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
550 ))),
551 response.iter().next()
552 );
553 };
554
555 let thread_one = thread::spawn(move || {
556 test_fn(resolver_one);
557 });
558
559 let thread_two = thread::spawn(move || {
560 test_fn(resolver_two);
561 });
562
563 thread_one.join().expect("thread_one failed");
564 thread_two.join().expect("thread_two failed");
565 }
566
567 #[cfg(feature = "dnssec")]
569 #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
570 pub fn sec_lookup_test<E: Executor + Send + 'static, R: ConnectionProvider>(
571 mut exec: E,
572 handle: R,
573 ) {
574 let resolver = AsyncResolver::new(
577 ResolverConfig::default(),
578 ResolverOpts {
579 validate: true,
580 try_tcp_on_error: true,
581 ..ResolverOpts::default()
582 },
583 handle,
584 );
585
586 let response = exec
587 .block_on(resolver.lookup_ip("www.example.com."))
588 .expect("failed to run lookup");
589
590 for address in response.iter() {
593 if address.is_ipv4() {
594 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
595 } else {
596 assert_eq!(
597 address,
598 IpAddr::V6(Ipv6Addr::new(
599 0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
600 ))
601 );
602 }
603 }
604 }
605
606 #[allow(deprecated)]
608 #[cfg(feature = "dnssec")]
609 #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
610 pub fn sec_lookup_fails_test<E: Executor + Send + 'static, R: ConnectionProvider>(
611 mut exec: E,
612 handle: R,
613 ) {
614 use crate::error::*;
615 use proto::rr::RecordType;
616 let resolver = AsyncResolver::new(
617 ResolverConfig::default(),
618 ResolverOpts {
619 validate: true,
620 ip_strategy: LookupIpStrategy::Ipv4Only,
621 ..ResolverOpts::default()
622 },
623 handle,
624 );
625
626 let response = exec.block_on(resolver.lookup_ip("trust-dns.org."));
628
629 assert!(response.is_err());
630 let error = response.unwrap_err();
631
632 use proto::error::{ProtoError, ProtoErrorKind};
633
634 let error_str = format!("{error}");
635 let name = Name::from_str("trust-dns.org.").unwrap();
636 let expected_str = format!(
637 "{}",
638 ResolveError::from(ProtoError::from(ProtoErrorKind::RrsigsNotPresent {
639 name,
640 record_type: RecordType::A
641 }))
642 );
643 assert_eq!(error_str, expected_str);
644 if let ResolveErrorKind::Proto(_) = *error.kind() {
645 } else {
646 panic!("wrong error")
647 }
648 }
649
650 #[cfg(feature = "system-config")]
652 #[cfg_attr(docsrs, doc(cfg(feature = "system-config")))]
653 pub fn system_lookup_test<E: Executor + Send + 'static, R: ConnectionProvider>(
654 mut exec: E,
655 handle: R,
656 ) {
657 let resolver =
658 AsyncResolver::<R>::from_system_conf(handle).expect("failed to create resolver");
659
660 let response = exec
661 .block_on(resolver.lookup_ip("www.example.com."))
662 .expect("failed to run lookup");
663
664 assert_eq!(response.iter().count(), 2);
665 for address in response.iter() {
666 if address.is_ipv4() {
667 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
668 } else {
669 assert_eq!(
670 address,
671 IpAddr::V6(Ipv6Addr::new(
672 0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
673 ))
674 );
675 }
676 }
677 }
678
679 #[cfg(feature = "system-config")]
681 #[cfg_attr(docsrs, doc(cfg(feature = "system-config")))]
682 pub fn hosts_lookup_test<E: Executor + Send + 'static, R: ConnectionProvider>(
683 mut exec: E,
684 handle: R,
685 ) {
686 let resolver =
687 AsyncResolver::<R>::from_system_conf(handle).expect("failed to create resolver");
688
689 let response = exec
690 .block_on(resolver.lookup_ip("a.com"))
691 .expect("failed to run lookup");
692
693 assert_eq!(response.iter().count(), 1);
694 for address in response.iter() {
695 if address.is_ipv4() {
696 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(10, 1, 0, 104)));
697 } else {
698 panic!("failed to run lookup");
699 }
700 }
701 }
702
703 pub fn fqdn_test<E: Executor + Send + 'static, R: ConnectionProvider>(mut exec: E, handle: R) {
705 let domain = Name::from_str("incorrect.example.com.").unwrap();
706 let search = vec![
707 Name::from_str("bad.example.com.").unwrap(),
708 Name::from_str("wrong.example.com.").unwrap(),
709 ];
710 let name_servers: Vec<NameServerConfig> =
711 ResolverConfig::default().name_servers().to_owned();
712
713 let resolver = AsyncResolver::<R>::new(
714 ResolverConfig::from_parts(Some(domain), search, name_servers),
715 ResolverOpts {
716 ip_strategy: LookupIpStrategy::Ipv4Only,
717 ..ResolverOpts::default()
718 },
719 handle,
720 );
721
722 let response = exec
723 .block_on(resolver.lookup_ip("www.example.com."))
724 .expect("failed to run lookup");
725
726 assert_eq!(response.iter().count(), 1);
727 for address in response.iter() {
728 if address.is_ipv4() {
729 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
730 } else {
731 panic!("should only be looking up IPv4");
732 }
733 }
734 }
735
736 pub fn ndots_test<E: Executor + Send + 'static, R: ConnectionProvider>(mut exec: E, handle: R) {
738 let domain = Name::from_str("incorrect.example.com.").unwrap();
739 let search = vec![
740 Name::from_str("bad.example.com.").unwrap(),
741 Name::from_str("wrong.example.com.").unwrap(),
742 ];
743 let name_servers: Vec<NameServerConfig> =
744 ResolverConfig::default().name_servers().to_owned();
745
746 let resolver = AsyncResolver::<R>::new(
747 ResolverConfig::from_parts(Some(domain), search, name_servers),
748 ResolverOpts {
749 ndots: 2,
751 ip_strategy: LookupIpStrategy::Ipv4Only,
752 ..ResolverOpts::default()
753 },
754 handle,
755 );
756
757 let response = exec
759 .block_on(resolver.lookup_ip("www.example.com"))
760 .expect("failed to run lookup");
761
762 assert_eq!(response.iter().count(), 1);
763 for address in response.iter() {
764 if address.is_ipv4() {
765 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
766 } else {
767 panic!("should only be looking up IPv4");
768 }
769 }
770 }
771
772 pub fn large_ndots_test<E: Executor + Send + 'static, R: ConnectionProvider>(
774 mut exec: E,
775 handle: R,
776 ) {
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 resolver = AsyncResolver::<R>::new(
786 ResolverConfig::from_parts(Some(domain), search, name_servers),
787 ResolverOpts {
788 ndots: 5,
790 ip_strategy: LookupIpStrategy::Ipv4Only,
791 ..ResolverOpts::default()
792 },
793 handle,
794 );
795
796 let response = exec
798 .block_on(resolver.lookup_ip("www.example.com"))
799 .expect("failed to run lookup");
800
801 assert_eq!(response.iter().count(), 1);
802 for address in response.iter() {
803 if address.is_ipv4() {
804 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
805 } else {
806 panic!("should only be looking up IPv4");
807 }
808 }
809 }
810
811 pub fn domain_search_test<E: Executor + Send + 'static, R: ConnectionProvider>(
813 mut exec: E,
814 handle: R,
815 ) {
816 let domain = Name::from_str("example.com.").unwrap();
820 let search = vec![
821 Name::from_str("bad.example.com.").unwrap(),
822 Name::from_str("wrong.example.com.").unwrap(),
823 ];
824 let name_servers: Vec<NameServerConfig> =
825 ResolverConfig::default().name_servers().to_owned();
826
827 let resolver = AsyncResolver::<R>::new(
828 ResolverConfig::from_parts(Some(domain), search, name_servers),
829 ResolverOpts {
830 ip_strategy: LookupIpStrategy::Ipv4Only,
831 ..ResolverOpts::default()
832 },
833 handle,
834 );
835
836 let response = exec
838 .block_on(resolver.lookup_ip("www"))
839 .expect("failed to run lookup");
840
841 assert_eq!(response.iter().count(), 1);
842 for address in response.iter() {
843 if address.is_ipv4() {
844 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
845 } else {
846 panic!("should only be looking up IPv4");
847 }
848 }
849 }
850
851 pub fn search_list_test<E: Executor + Send + 'static, R: ConnectionProvider>(
853 mut exec: E,
854 handle: R,
855 ) {
856 let domain = Name::from_str("incorrect.example.com.").unwrap();
857 let search = vec![
858 Name::from_str("bad.example.com.").unwrap(),
860 Name::from_str("example.com.").unwrap(),
862 ];
863 let name_servers: Vec<NameServerConfig> =
864 ResolverConfig::default().name_servers().to_owned();
865
866 let resolver = AsyncResolver::<R>::new(
867 ResolverConfig::from_parts(Some(domain), search, name_servers),
868 ResolverOpts {
869 ip_strategy: LookupIpStrategy::Ipv4Only,
870 ..ResolverOpts::default()
871 },
872 handle,
873 );
874
875 let response = exec
877 .block_on(resolver.lookup_ip("www"))
878 .expect("failed to run lookup");
879
880 assert_eq!(response.iter().count(), 1);
881 for address in response.iter() {
882 if address.is_ipv4() {
883 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
884 } else {
885 panic!("should only be looking up IPv4");
886 }
887 }
888 }
889
890 pub fn idna_test<E: Executor + Send + 'static, R: ConnectionProvider>(mut exec: E, handle: R) {
892 let resolver =
893 AsyncResolver::<R>::new(ResolverConfig::default(), ResolverOpts::default(), handle);
894
895 let response = exec
896 .block_on(resolver.lookup_ip("中国.icom.museum."))
897 .expect("failed to run lookup");
898
899 assert!(response.iter().next().is_some());
902 }
903
904 pub fn localhost_ipv4_test<E: Executor + Send + 'static, R: ConnectionProvider>(
906 mut exec: E,
907 handle: R,
908 ) {
909 let resolver = AsyncResolver::<R>::new(
910 ResolverConfig::default(),
911 ResolverOpts {
912 ip_strategy: LookupIpStrategy::Ipv4thenIpv6,
913 ..ResolverOpts::default()
914 },
915 handle,
916 );
917
918 let response = exec
919 .block_on(resolver.lookup_ip("localhost"))
920 .expect("failed to run lookup");
921
922 let mut iter = response.iter();
923 assert_eq!(
924 iter.next().expect("no A"),
925 IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
926 );
927 }
928
929 pub fn localhost_ipv6_test<E: Executor + Send + 'static, R: ConnectionProvider>(
931 mut exec: E,
932 handle: R,
933 ) {
934 let resolver = AsyncResolver::<R>::new(
935 ResolverConfig::default(),
936 ResolverOpts {
937 ip_strategy: LookupIpStrategy::Ipv6thenIpv4,
938 ..ResolverOpts::default()
939 },
940 handle,
941 );
942
943 let response = exec
944 .block_on(resolver.lookup_ip("localhost"))
945 .expect("failed to run lookup");
946
947 let mut iter = response.iter();
948 assert_eq!(
949 iter.next().expect("no AAAA"),
950 IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1,))
951 );
952 }
953
954 pub fn search_ipv4_large_ndots_test<E: Executor + Send + 'static, R: ConnectionProvider>(
956 mut exec: E,
957 handle: R,
958 ) {
959 let mut config = ResolverConfig::default();
960 config.add_search(Name::from_str("example.com").unwrap());
961
962 let resolver = AsyncResolver::<R>::new(
963 config,
964 ResolverOpts {
965 ip_strategy: LookupIpStrategy::Ipv4Only,
966 ndots: 5,
967 ..ResolverOpts::default()
968 },
969 handle,
970 );
971
972 let response = exec
973 .block_on(resolver.lookup_ip("198.51.100.35"))
974 .expect("failed to run lookup");
975
976 let mut iter = response.iter();
977 assert_eq!(
978 iter.next().expect("no rdatas"),
979 IpAddr::V4(Ipv4Addr::new(198, 51, 100, 35))
980 );
981 }
982
983 pub fn search_ipv6_large_ndots_test<E: Executor + Send + 'static, R: ConnectionProvider>(
985 mut exec: E,
986 handle: R,
987 ) {
988 let mut config = ResolverConfig::default();
989 config.add_search(Name::from_str("example.com").unwrap());
990
991 let resolver = AsyncResolver::<R>::new(
992 config,
993 ResolverOpts {
994 ip_strategy: LookupIpStrategy::Ipv4Only,
995 ndots: 5,
996 ..ResolverOpts::default()
997 },
998 handle,
999 );
1000
1001 let response = exec
1002 .block_on(resolver.lookup_ip("2001:db8::c633:6423"))
1003 .expect("failed to run lookup");
1004
1005 let mut iter = response.iter();
1006 assert_eq!(
1007 iter.next().expect("no rdatas"),
1008 IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc633, 0x6423))
1009 );
1010 }
1011
1012 pub fn search_ipv6_name_parse_fails_test<
1014 E: Executor + Send + 'static,
1015 R: ConnectionProvider,
1016 >(
1017 mut exec: E,
1018 handle: R,
1019 ) {
1020 let mut config = ResolverConfig::default();
1021 config.add_search(Name::from_str("example.com").unwrap());
1022
1023 let resolver = AsyncResolver::<R>::new(
1024 config,
1025 ResolverOpts {
1026 ip_strategy: LookupIpStrategy::Ipv4Only,
1027 ndots: 5,
1028 ..ResolverOpts::default()
1029 },
1030 handle,
1031 );
1032
1033 let response = exec
1034 .block_on(resolver.lookup_ip("2001:db8::198.51.100.35"))
1035 .expect("failed to run lookup");
1036
1037 let mut iter = response.iter();
1038 assert_eq!(
1039 iter.next().expect("no rdatas"),
1040 IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc633, 0x6423))
1041 );
1042 }
1043}
1044#[cfg(test)]
1045#[cfg(feature = "tokio-runtime")]
1046#[allow(clippy::extra_unused_type_parameters)]
1047mod tests {
1048 use proto::xfer::DnsRequest;
1049 use tokio::runtime::Runtime;
1050
1051 use crate::config::{ResolverConfig, ResolverOpts};
1052 use crate::name_server::GenericConnection;
1053
1054 use super::*;
1055
1056 fn is_send_t<T: Send>() -> bool {
1057 true
1058 }
1059
1060 fn is_sync_t<T: Sync>() -> bool {
1061 true
1062 }
1063
1064 #[test]
1065 fn test_send_sync() {
1066 assert!(is_send_t::<ResolverConfig>());
1067 assert!(is_sync_t::<ResolverConfig>());
1068 assert!(is_send_t::<ResolverOpts>());
1069 assert!(is_sync_t::<ResolverOpts>());
1070
1071 assert!(is_send_t::<AsyncResolver<TokioConnectionProvider>>());
1072 assert!(is_sync_t::<AsyncResolver<TokioConnectionProvider>>());
1073
1074 assert!(is_send_t::<DnsRequest>());
1075 assert!(is_send_t::<LookupIpFuture<GenericConnection, ResolveError>>());
1076 assert!(is_send_t::<LookupFuture<GenericConnection, ResolveError>>());
1077 }
1078
1079 #[test]
1080 fn test_lookup_google() {
1081 use super::testing::lookup_test;
1082 let io_loop = Runtime::new().expect("failed to create tokio runtime");
1083 let handle = TokioConnectionProvider::default();
1084 lookup_test::<Runtime, TokioConnectionProvider>(ResolverConfig::google(), io_loop, handle)
1085 }
1086
1087 #[test]
1088 fn test_lookup_cloudflare() {
1089 use super::testing::lookup_test;
1090 let io_loop = Runtime::new().expect("failed to create tokio runtime");
1091 let handle = TokioConnectionProvider::default();
1092 lookup_test::<Runtime, TokioConnectionProvider>(
1093 ResolverConfig::cloudflare(),
1094 io_loop,
1095 handle,
1096 )
1097 }
1098
1099 #[test]
1100 fn test_lookup_quad9() {
1101 use super::testing::lookup_test;
1102 let io_loop = Runtime::new().expect("failed to create tokio runtime");
1103 let handle = TokioConnectionProvider::default();
1104 lookup_test::<Runtime, TokioConnectionProvider>(ResolverConfig::quad9(), io_loop, handle)
1105 }
1106
1107 #[test]
1108 fn test_ip_lookup() {
1109 use super::testing::ip_lookup_test;
1110 let io_loop = Runtime::new().expect("failed to create tokio runtime");
1111 let handle = TokioConnectionProvider::default();
1112 ip_lookup_test::<Runtime, TokioConnectionProvider>(io_loop, handle)
1113 }
1114
1115 #[test]
1116 fn test_ip_lookup_across_threads() {
1117 use super::testing::ip_lookup_across_threads_test;
1118 let _io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1119 let handle = TokioConnectionProvider::default();
1120 ip_lookup_across_threads_test::<Runtime, TokioConnectionProvider>(handle)
1121 }
1122
1123 #[test]
1124 #[cfg(feature = "dnssec")]
1125 fn test_sec_lookup() {
1126 use super::testing::sec_lookup_test;
1127 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1128 let handle = TokioConnectionProvider::default();
1129 sec_lookup_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1130 }
1131
1132 #[test]
1133 #[cfg(feature = "dnssec")]
1134 fn test_sec_lookup_fails() {
1135 use super::testing::sec_lookup_fails_test;
1136 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1137 let handle = TokioConnectionProvider::default();
1138 sec_lookup_fails_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1139 }
1140
1141 #[test]
1142 #[ignore]
1143 #[cfg(any(unix, target_os = "windows"))]
1144 #[cfg(feature = "system-config")]
1145 fn test_system_lookup() {
1146 use super::testing::system_lookup_test;
1147 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1148 let handle = TokioConnectionProvider::default();
1149 system_lookup_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1150 }
1151
1152 #[test]
1153 #[ignore]
1154 #[cfg(unix)]
1156 #[cfg(feature = "system-config")]
1157 fn test_hosts_lookup() {
1158 use super::testing::hosts_lookup_test;
1159 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1160 let handle = TokioConnectionProvider::default();
1161 hosts_lookup_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1162 }
1163
1164 #[test]
1165 fn test_fqdn() {
1166 use super::testing::fqdn_test;
1167 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1168 let handle = TokioConnectionProvider::default();
1169 fqdn_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1170 }
1171
1172 #[test]
1173 fn test_ndots() {
1174 use super::testing::ndots_test;
1175 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1176 let handle = TokioConnectionProvider::default();
1177 ndots_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1178 }
1179
1180 #[test]
1181 fn test_large_ndots() {
1182 use super::testing::large_ndots_test;
1183 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1184 let handle = TokioConnectionProvider::default();
1185 large_ndots_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1186 }
1187
1188 #[test]
1189 fn test_domain_search() {
1190 use super::testing::domain_search_test;
1191 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1192 let handle = TokioConnectionProvider::default();
1193 domain_search_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1194 }
1195
1196 #[test]
1197 fn test_search_list() {
1198 use super::testing::search_list_test;
1199 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1200 let handle = TokioConnectionProvider::default();
1201 search_list_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1202 }
1203
1204 #[test]
1205 fn test_idna() {
1206 use super::testing::idna_test;
1207 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1208 let handle = TokioConnectionProvider::default();
1209 idna_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1210 }
1211
1212 #[test]
1213 fn test_localhost_ipv4() {
1214 use super::testing::localhost_ipv4_test;
1215 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1216 let handle = TokioConnectionProvider::default();
1217 localhost_ipv4_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1218 }
1219
1220 #[test]
1221 fn test_localhost_ipv6() {
1222 use super::testing::localhost_ipv6_test;
1223 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1224 let handle = TokioConnectionProvider::default();
1225 localhost_ipv6_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1226 }
1227
1228 #[test]
1229 fn test_search_ipv4_large_ndots() {
1230 use super::testing::search_ipv4_large_ndots_test;
1231 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1232 let handle = TokioConnectionProvider::default();
1233 search_ipv4_large_ndots_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1234 }
1235
1236 #[test]
1237 fn test_search_ipv6_large_ndots() {
1238 use super::testing::search_ipv6_large_ndots_test;
1239 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1240 let handle = TokioConnectionProvider::default();
1241 search_ipv6_large_ndots_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1242 }
1243
1244 #[test]
1245 fn test_search_ipv6_name_parse_fails() {
1246 use super::testing::search_ipv6_name_parse_fails_test;
1247 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1248 let handle = TokioConnectionProvider::default();
1249 search_ipv6_name_parse_fails_test::<Runtime, TokioConnectionProvider>(io_loop, handle);
1250 }
1251
1252 #[test]
1253 fn test_build_names_onion() {
1254 let handle = TokioConnectionProvider::default();
1255 let mut config = ResolverConfig::default();
1256 config.add_search(Name::from_ascii("example.com.").unwrap());
1257 let resolver =
1258 AsyncResolver::<TokioConnectionProvider>::new(config, ResolverOpts::default(), handle);
1259 let tor_address = [
1260 Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion")
1261 .unwrap(),
1262 Name::from_ascii("www.2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion")
1263 .unwrap(), ];
1265 let not_tor_address = [
1266 Name::from_ascii("onion").unwrap(),
1267 Name::from_ascii("www.onion").unwrap(),
1268 Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.www.onion")
1269 .unwrap(), Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion.to")
1271 .unwrap(), ];
1273 for name in &tor_address {
1274 assert_eq!(resolver.build_names(name.clone()).len(), 1);
1275 }
1276 for name in ¬_tor_address {
1277 assert_eq!(resolver.build_names(name.clone()).len(), 2);
1278 }
1279 }
1280}