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 proto::DnsHandle;
20use tracing::{debug, trace};
21
22use crate::caching_client::CachingClient;
23use crate::config::{ResolverConfig, ResolverOpts};
24use crate::dns_lru::{self, DnsLru};
25use crate::error::*;
26use crate::lookup::{self, Lookup, LookupEither, LookupFuture};
27use crate::lookup_ip::{LookupIp, LookupIpFuture};
28use crate::name_server::{
29 ConnectionProvider, GenericConnection, GenericConnectionProvider, NameServerPool,
30 NameServerStats, RuntimeProvider,
31};
32#[cfg(feature = "tokio-runtime")]
33use crate::name_server::{TokioConnection, TokioConnectionProvider, TokioHandle};
34
35use crate::Hosts;
36
37#[derive(Clone)]
64pub struct AsyncResolver<C: DnsHandle<Error = ResolveError>, P: ConnectionProvider<Conn = C>> {
65 config: ResolverConfig,
66 options: ResolverOpts,
67 client_cache: CachingClient<LookupEither<C, P>, ResolveError>,
68 hosts: Option<Arc<Hosts>>,
69}
70
71impl<C, P> AsyncResolver<C, P>
72where
73 C: DnsHandle<Error = ResolveError>,
74 P: ConnectionProvider<Conn = C>,
75{
76 pub fn name_server_stats(&self) -> Vec<NameServerStats> {
77 self.client_cache.client().pool().name_server_stats()
78 }
79}
80
81#[cfg(feature = "tokio-runtime")]
83#[cfg_attr(docsrs, doc(cfg(feature = "tokio-runtime")))]
84pub type TokioAsyncResolver = AsyncResolver<TokioConnection, TokioConnectionProvider>;
85
86macro_rules! lookup_fn {
87 ($p:ident, $l:ty, $r:path) => {
88 pub async fn $p<N: IntoName>(&self, query: N) -> Result<$l, ResolveError> {
96 let name = match query.into_name() {
97 Ok(name) => name,
98 Err(err) => {
99 return Err(err.into());
100 }
101 };
102
103 self.inner_lookup(name, $r, self.request_options()).await
104 }
105 };
106 ($p:ident, $l:ty, $r:path, $t:ty) => {
107 pub async fn $p(&self, query: $t) -> Result<$l, ResolveError> {
113 let name = Name::from(query);
114 self.inner_lookup(name, $r, self.request_options()).await
115 }
116 };
117}
118
119#[cfg(feature = "tokio-runtime")]
120#[cfg_attr(docsrs, doc(cfg(feature = "tokio-runtime")))]
121impl TokioAsyncResolver {
122 pub fn tokio(config: ResolverConfig, options: ResolverOpts) -> Result<Self, ResolveError> {
136 Self::new(config, options, TokioHandle)
137 }
138
139 #[cfg(any(unix, target_os = "windows"))]
143 #[cfg(feature = "system-config")]
144 #[cfg_attr(
145 docsrs,
146 doc(cfg(all(feature = "system-config", any(unix, target_os = "windows"))))
147 )]
148 pub fn tokio_from_system_conf() -> Result<Self, ResolveError> {
149 Self::from_system_conf(TokioHandle)
150 }
151}
152
153impl<R: RuntimeProvider> AsyncResolver<GenericConnection, GenericConnectionProvider<R>> {
154 pub fn new(
170 config: ResolverConfig,
171 options: ResolverOpts,
172 runtime: R::Handle,
173 ) -> Result<Self, ResolveError> {
174 Self::new_with_conn(
175 config,
176 options,
177 GenericConnectionProvider::<R>::new(runtime),
178 )
179 }
180
181 #[cfg(any(unix, target_os = "windows"))]
187 #[cfg(feature = "system-config")]
188 #[cfg_attr(
189 docsrs,
190 doc(cfg(all(feature = "system-config", any(unix, target_os = "windows"))))
191 )]
192 pub fn from_system_conf(runtime: R::Handle) -> Result<Self, ResolveError> {
193 Self::from_system_conf_with_provider(GenericConnectionProvider::<R>::new(runtime))
194 }
195
196 pub fn clear_cache(&self) {
198 self.client_cache.clear_cache();
199 }
200}
201
202impl<C: DnsHandle<Error = ResolveError>, P: ConnectionProvider<Conn = C>> AsyncResolver<C, P> {
203 #[allow(clippy::unnecessary_wraps)]
217 pub fn new_with_conn(
218 config: ResolverConfig,
219 options: ResolverOpts,
220 conn_provider: P,
221 ) -> Result<Self, ResolveError> {
222 let pool = NameServerPool::from_config_with_provider(&config, &options, conn_provider);
223 let either;
224 let client = RetryDnsHandle::new(pool, options.attempts);
225 if options.validate {
226 #[cfg(feature = "dnssec")]
227 {
228 use proto::xfer::DnssecDnsHandle;
229 either = LookupEither::Secure(DnssecDnsHandle::new(client));
230 }
231
232 #[cfg(not(feature = "dnssec"))]
233 {
234 tracing::warn!("validate option is only available with 'dnssec' feature");
236 either = LookupEither::Retry(client);
237 }
238 } else {
239 either = LookupEither::Retry(client);
240 }
241
242 let hosts = if options.use_hosts_file {
243 Some(Arc::new(Hosts::new()))
244 } else {
245 None
246 };
247
248 trace!("handle passed back");
249 let lru = DnsLru::new(options.cache_size, dns_lru::TtlConfig::from_opts(&options));
250 Ok(Self {
251 config,
252 options,
253 client_cache: CachingClient::with_cache(lru, either, options.preserve_intermediates),
254 hosts,
255 })
256 }
257
258 #[cfg(any(unix, target_os = "windows"))]
262 #[cfg(feature = "system-config")]
263 #[cfg_attr(
264 docsrs,
265 doc(cfg(all(feature = "system-config", any(unix, target_os = "windows"))))
266 )]
267 pub fn from_system_conf_with_provider(conn_provider: P) -> Result<Self, ResolveError> {
268 let (config, options) = super::system_conf::read_system_conf()?;
269 Self::new_with_conn(config, options, conn_provider)
270 }
271
272 pub(crate) fn request_options(&self) -> DnsRequestOptions {
274 let mut request_opts = DnsRequestOptions::default();
275 request_opts.recursion_desired = self.options.recursion_desired;
276 request_opts.use_edns = self.options.edns0;
277
278 request_opts
279 }
280
281 pub async fn lookup<N: IntoName>(
294 &self,
295 name: N,
296 record_type: RecordType,
297 ) -> Result<Lookup, ResolveError> {
298 let name = match name.into_name() {
299 Ok(name) => name,
300 Err(err) => return Err(err.into()),
301 };
302
303 self.inner_lookup(name, record_type, self.request_options())
304 .await
305 }
306
307 fn push_name(name: Name, names: &mut Vec<Name>) {
308 if !names.contains(&name) {
309 names.push(name);
310 }
311 }
312
313 fn build_names(&self, name: Name) -> Vec<Name> {
314 if name.is_fqdn()
316 || ONION.zone_of(&name)
317 && name
318 .trim_to(2)
319 .iter()
320 .next()
321 .map(|name| name.len() == 56) .unwrap_or(false)
323 {
324 vec![name]
327 } else {
328 let mut names =
331 Vec::<Name>::with_capacity(1 + 1 + self.config.search().len());
332
333 let raw_name_first: bool =
335 name.num_labels() as usize > self.options.ndots || name.is_localhost();
336
337 if !raw_name_first {
339 names.push(name.clone());
340 }
341
342 for search in self.config.search().iter().rev() {
343 let name_search = name.clone().append_domain(search);
344
345 match name_search {
346 Ok(name_search) => Self::push_name(name_search, &mut names),
347 Err(e) => debug!(
348 "Not adding {} to {} for search due to error: {}",
349 search, name, e
350 ),
351 }
352 }
353
354 if let Some(domain) = self.config.domain() {
355 let name_search = name.clone().append_domain(domain);
356
357 match name_search {
358 Ok(name_search) => Self::push_name(name_search, &mut names),
359 Err(e) => debug!(
360 "Not adding {} to {} for search due to error: {}",
361 domain, name, e
362 ),
363 }
364 }
365
366 if raw_name_first {
368 names.push(name);
370 }
371
372 names
373 }
374 }
375
376 pub(crate) async fn inner_lookup<L>(
377 &self,
378 name: Name,
379 record_type: RecordType,
380 options: DnsRequestOptions,
381 ) -> Result<L, ResolveError>
382 where
383 L: From<Lookup> + Send + 'static,
384 {
385 let names = self.build_names(name);
386 LookupFuture::lookup(names, record_type, options, self.client_cache.clone())
387 .await
388 .map(L::from)
389 }
390
391 pub async fn lookup_ip<N: IntoName + TryParseIp>(
398 &self,
399 host: N,
400 ) -> Result<LookupIp, ResolveError> {
401 let mut finally_ip_addr: Option<Record> = None;
402 let maybe_ip = host.try_parse_ip();
403 let maybe_name: ProtoResult<Name> = host.into_name();
404
405 if let Some(ip_addr) = maybe_ip {
407 let name = maybe_name.clone().unwrap_or_default();
408 let record = Record::from_rdata(name.clone(), dns_lru::MAX_TTL, ip_addr.clone());
409
410 if self.options.ndots > 4 {
415 finally_ip_addr = Some(record);
416 } else {
417 let query = Query::query(name, ip_addr.to_record_type());
418 let lookup = Lookup::new_with_max_ttl(query, Arc::from([record]));
419 return Ok(lookup.into());
420 }
421 }
422
423 let name = match (maybe_name, finally_ip_addr.as_ref()) {
424 (Ok(name), _) => name,
425 (Err(_), Some(ip_addr)) => {
426 let query = Query::query(ip_addr.name().clone(), ip_addr.record_type());
428 let lookup = Lookup::new_with_max_ttl(query, Arc::from([ip_addr.clone()]));
429 return Ok(lookup.into());
430 }
431 (Err(err), None) => {
432 return Err(err.into());
433 }
434 };
435
436 let names = self.build_names(name);
437 let hosts = self.hosts.as_ref().cloned();
438
439 LookupIpFuture::lookup(
440 names,
441 self.options.ip_strategy,
442 self.client_cache.clone(),
443 self.request_options(),
444 hosts,
445 finally_ip_addr.and_then(Record::into_data),
446 )
447 .await
448 }
449
450 pub fn set_hosts(&mut self, hosts: Option<Hosts>) {
452 self.hosts = hosts.map(Arc::new);
453 }
454
455 lookup_fn!(
456 reverse_lookup,
457 lookup::ReverseLookup,
458 RecordType::PTR,
459 IpAddr
460 );
461 lookup_fn!(ipv4_lookup, lookup::Ipv4Lookup, RecordType::A);
462 lookup_fn!(ipv6_lookup, lookup::Ipv6Lookup, RecordType::AAAA);
463 lookup_fn!(mx_lookup, lookup::MxLookup, RecordType::MX);
464 lookup_fn!(ns_lookup, lookup::NsLookup, RecordType::NS);
465 lookup_fn!(soa_lookup, lookup::SoaLookup, RecordType::SOA);
466 lookup_fn!(srv_lookup, lookup::SrvLookup, RecordType::SRV);
467 lookup_fn!(tlsa_lookup, lookup::TlsaLookup, RecordType::TLSA);
468 lookup_fn!(txt_lookup, lookup::TxtLookup, RecordType::TXT);
469}
470
471impl<C: DnsHandle<Error = ResolveError>, P: ConnectionProvider<Conn = C>> fmt::Debug
472 for AsyncResolver<C, P>
473{
474 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
475 f.debug_struct("AsyncResolver")
476 .field("request_tx", &"...")
477 .finish()
478 }
479}
480
481#[cfg(any(test, feature = "testing"))]
483#[cfg_attr(docsrs, doc(cfg(feature = "testing")))]
484#[allow(dead_code, unreachable_pub)]
485pub mod testing {
486 use std::{net::*, str::FromStr};
487
488 use crate::config::{LookupIpStrategy, NameServerConfig, ResolverConfig, ResolverOpts};
489 use crate::name_server::{GenericConnection, GenericConnectionProvider, RuntimeProvider};
490 use crate::AsyncResolver;
491 use proto::{rr::Name, Executor};
492
493 pub fn lookup_test<E: Executor, R: RuntimeProvider>(
495 config: ResolverConfig,
496 mut exec: E,
497 handle: R::Handle,
498 ) {
499 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
500 config,
501 ResolverOpts::default(),
502 handle,
503 )
504 .expect("failed to create resolver");
505
506 let response = exec
507 .block_on(resolver.lookup_ip("www.example.com."))
508 .expect("failed to run lookup");
509
510 assert_eq!(response.iter().count(), 1);
511 for address in response.iter() {
512 if address.is_ipv4() {
513 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
514 } else {
515 assert_eq!(
516 address,
517 IpAddr::V6(Ipv6Addr::new(
518 0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
519 ))
520 );
521 }
522 }
523 }
524
525 pub fn ip_lookup_test<E: Executor, R: RuntimeProvider>(mut exec: E, handle: R::Handle) {
527 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
528 ResolverConfig::default(),
529 ResolverOpts::default(),
530 handle,
531 )
532 .expect("failed to create resolver");
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 pub fn ip_lookup_across_threads_test<E: Executor + Send + 'static, R: RuntimeProvider>(
557 handle: R::Handle,
558 ) {
559 use std::thread;
563 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
564 ResolverConfig::default(),
565 ResolverOpts::default(),
566 handle,
567 )
568 .expect("failed to create resolver");
569
570 let resolver_one = resolver.clone();
571 let resolver_two = resolver;
572
573 let test_fn = |resolver: AsyncResolver<GenericConnection, GenericConnectionProvider<R>>| {
574 let mut exec = E::new();
575
576 let response = exec
577 .block_on(resolver.lookup_ip("10.1.0.2"))
578 .expect("failed to run lookup");
579
580 assert_eq!(
581 Some(IpAddr::V4(Ipv4Addr::new(10, 1, 0, 2))),
582 response.iter().next()
583 );
584
585 let response = exec
586 .block_on(resolver.lookup_ip("2606:2800:220:1:248:1893:25c8:1946"))
587 .expect("failed to run lookup");
588
589 assert_eq!(
590 Some(IpAddr::V6(Ipv6Addr::new(
591 0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
592 ))),
593 response.iter().next()
594 );
595 };
596
597 let thread_one = thread::spawn(move || {
598 test_fn(resolver_one);
599 });
600
601 let thread_two = thread::spawn(move || {
602 test_fn(resolver_two);
603 });
604
605 thread_one.join().expect("thread_one failed");
606 thread_two.join().expect("thread_two failed");
607 }
608
609 #[cfg(feature = "dnssec")]
611 #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
612 pub fn sec_lookup_test<E: Executor + Send + 'static, R: RuntimeProvider>(
613 mut exec: E,
614 handle: R::Handle,
615 ) {
616 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
619 ResolverConfig::default(),
620 ResolverOpts {
621 validate: true,
622 try_tcp_on_error: true,
623 ..ResolverOpts::default()
624 },
625 handle,
626 )
627 .expect("failed to create resolver");
628
629 let response = exec
630 .block_on(resolver.lookup_ip("www.example.com."))
631 .expect("failed to run lookup");
632
633 for address in response.iter() {
636 if address.is_ipv4() {
637 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
638 } else {
639 assert_eq!(
640 address,
641 IpAddr::V6(Ipv6Addr::new(
642 0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
643 ))
644 );
645 }
646 }
647 }
648
649 #[allow(deprecated)]
651 #[cfg(feature = "dnssec")]
652 #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
653 pub fn sec_lookup_fails_test<E: Executor + Send + 'static, R: RuntimeProvider>(
654 mut exec: E,
655 handle: R::Handle,
656 ) {
657 use crate::error::*;
658 use proto::rr::RecordType;
659 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
660 ResolverConfig::default(),
661 ResolverOpts {
662 validate: true,
663 ip_strategy: LookupIpStrategy::Ipv4Only,
664 ..ResolverOpts::default()
665 },
666 handle,
667 )
668 .expect("failed to create resolver");
669
670 let response = exec.block_on(resolver.lookup_ip("trust-dns.org."));
672
673 assert!(response.is_err());
674 let error = response.unwrap_err();
675
676 use proto::error::{ProtoError, ProtoErrorKind};
677
678 let error_str = format!("{}", error);
679 let name = Name::from_str("trust-dns.org.").unwrap();
680 let expected_str = format!(
681 "{}",
682 ResolveError::from(ProtoError::from(ProtoErrorKind::RrsigsNotPresent {
683 name,
684 record_type: RecordType::A
685 }))
686 );
687 assert_eq!(error_str, expected_str);
688 if let ResolveErrorKind::Proto(_) = *error.kind() {
689 } else {
690 panic!("wrong error")
691 }
692 }
693
694 #[cfg(feature = "system-config")]
696 #[cfg_attr(docsrs, doc(cfg(feature = "system-config")))]
697 pub fn system_lookup_test<E: Executor + Send + 'static, R: RuntimeProvider>(
698 mut exec: E,
699 handle: R::Handle,
700 ) {
701 let resolver =
702 AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::from_system_conf(
703 handle,
704 )
705 .expect("failed to create resolver");
706
707 let response = exec
708 .block_on(resolver.lookup_ip("www.example.com."))
709 .expect("failed to run lookup");
710
711 assert_eq!(response.iter().count(), 2);
712 for address in response.iter() {
713 if address.is_ipv4() {
714 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
715 } else {
716 assert_eq!(
717 address,
718 IpAddr::V6(Ipv6Addr::new(
719 0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
720 ))
721 );
722 }
723 }
724 }
725
726 #[cfg(feature = "system-config")]
728 #[cfg_attr(docsrs, doc(cfg(feature = "system-config")))]
729 pub fn hosts_lookup_test<E: Executor + Send + 'static, R: RuntimeProvider>(
730 mut exec: E,
731 handle: R::Handle,
732 ) {
733 let resolver =
734 AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::from_system_conf(
735 handle,
736 )
737 .expect("failed to create resolver");
738
739 let response = exec
740 .block_on(resolver.lookup_ip("a.com"))
741 .expect("failed to run lookup");
742
743 assert_eq!(response.iter().count(), 1);
744 for address in response.iter() {
745 if address.is_ipv4() {
746 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(10, 1, 0, 104)));
747 } else {
748 panic!("failed to run lookup");
749 }
750 }
751 }
752
753 pub fn fqdn_test<E: Executor + Send + 'static, R: RuntimeProvider>(
755 mut exec: E,
756 handle: R::Handle,
757 ) {
758 let domain = Name::from_str("incorrect.example.com.").unwrap();
759 let search = vec![
760 Name::from_str("bad.example.com.").unwrap(),
761 Name::from_str("wrong.example.com.").unwrap(),
762 ];
763 let name_servers: Vec<NameServerConfig> =
764 ResolverConfig::default().name_servers().to_owned();
765
766 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
767 ResolverConfig::from_parts(Some(domain), search, name_servers),
768 ResolverOpts {
769 ip_strategy: LookupIpStrategy::Ipv4Only,
770 ..ResolverOpts::default()
771 },
772 handle,
773 )
774 .expect("failed to create resolver");
775
776 let response = exec
777 .block_on(resolver.lookup_ip("www.example.com."))
778 .expect("failed to run lookup");
779
780 assert_eq!(response.iter().count(), 1);
781 for address in response.iter() {
782 if address.is_ipv4() {
783 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
784 } else {
785 panic!("should only be looking up IPv4");
786 }
787 }
788 }
789
790 pub fn ndots_test<E: Executor + Send + 'static, R: RuntimeProvider>(
792 mut exec: E,
793 handle: R::Handle,
794 ) {
795 let domain = Name::from_str("incorrect.example.com.").unwrap();
796 let search = vec![
797 Name::from_str("bad.example.com.").unwrap(),
798 Name::from_str("wrong.example.com.").unwrap(),
799 ];
800 let name_servers: Vec<NameServerConfig> =
801 ResolverConfig::default().name_servers().to_owned();
802
803 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
804 ResolverConfig::from_parts(Some(domain), search, name_servers),
805 ResolverOpts {
806 ndots: 2,
808 ip_strategy: LookupIpStrategy::Ipv4Only,
809 ..ResolverOpts::default()
810 },
811 handle,
812 )
813 .expect("failed to create resolver");
814
815 let response = exec
817 .block_on(resolver.lookup_ip("www.example.com"))
818 .expect("failed to run lookup");
819
820 assert_eq!(response.iter().count(), 1);
821 for address in response.iter() {
822 if address.is_ipv4() {
823 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
824 } else {
825 panic!("should only be looking up IPv4");
826 }
827 }
828 }
829
830 pub fn large_ndots_test<E: Executor + Send + 'static, R: RuntimeProvider>(
832 mut exec: E,
833 handle: R::Handle,
834 ) {
835 let domain = Name::from_str("incorrect.example.com.").unwrap();
836 let search = vec![
837 Name::from_str("bad.example.com.").unwrap(),
838 Name::from_str("wrong.example.com.").unwrap(),
839 ];
840 let name_servers: Vec<NameServerConfig> =
841 ResolverConfig::default().name_servers().to_owned();
842
843 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
844 ResolverConfig::from_parts(Some(domain), search, name_servers),
845 ResolverOpts {
846 ndots: 5,
848 ip_strategy: LookupIpStrategy::Ipv4Only,
849 ..ResolverOpts::default()
850 },
851 handle,
852 )
853 .expect("failed to create resolver");
854
855 let response = exec
857 .block_on(resolver.lookup_ip("www.example.com"))
858 .expect("failed to run lookup");
859
860 assert_eq!(response.iter().count(), 1);
861 for address in response.iter() {
862 if address.is_ipv4() {
863 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
864 } else {
865 panic!("should only be looking up IPv4");
866 }
867 }
868 }
869
870 pub fn domain_search_test<E: Executor + Send + 'static, R: RuntimeProvider>(
872 mut exec: E,
873 handle: R::Handle,
874 ) {
875 let domain = Name::from_str("example.com.").unwrap();
879 let search = vec![
880 Name::from_str("bad.example.com.").unwrap(),
881 Name::from_str("wrong.example.com.").unwrap(),
882 ];
883 let name_servers: Vec<NameServerConfig> =
884 ResolverConfig::default().name_servers().to_owned();
885
886 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
887 ResolverConfig::from_parts(Some(domain), search, name_servers),
888 ResolverOpts {
889 ip_strategy: LookupIpStrategy::Ipv4Only,
890 ..ResolverOpts::default()
891 },
892 handle,
893 )
894 .expect("failed to create resolver");
895
896 let response = exec
898 .block_on(resolver.lookup_ip("www"))
899 .expect("failed to run lookup");
900
901 assert_eq!(response.iter().count(), 1);
902 for address in response.iter() {
903 if address.is_ipv4() {
904 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
905 } else {
906 panic!("should only be looking up IPv4");
907 }
908 }
909 }
910
911 pub fn search_list_test<E: Executor + Send + 'static, R: RuntimeProvider>(
913 mut exec: E,
914 handle: R::Handle,
915 ) {
916 let domain = Name::from_str("incorrect.example.com.").unwrap();
917 let search = vec![
918 Name::from_str("bad.example.com.").unwrap(),
920 Name::from_str("example.com.").unwrap(),
922 ];
923 let name_servers: Vec<NameServerConfig> =
924 ResolverConfig::default().name_servers().to_owned();
925
926 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
927 ResolverConfig::from_parts(Some(domain), search, name_servers),
928 ResolverOpts {
929 ip_strategy: LookupIpStrategy::Ipv4Only,
930 ..ResolverOpts::default()
931 },
932 handle,
933 )
934 .expect("failed to create resolver");
935
936 let response = exec
938 .block_on(resolver.lookup_ip("www"))
939 .expect("failed to run lookup");
940
941 assert_eq!(response.iter().count(), 1);
942 for address in response.iter() {
943 if address.is_ipv4() {
944 assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
945 } else {
946 panic!("should only be looking up IPv4");
947 }
948 }
949 }
950
951 pub fn idna_test<E: Executor + Send + 'static, R: RuntimeProvider>(
953 mut exec: E,
954 handle: R::Handle,
955 ) {
956 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
957 ResolverConfig::default(),
958 ResolverOpts::default(),
959 handle,
960 )
961 .expect("failed to create resolver");
962
963 let response = exec
964 .block_on(resolver.lookup_ip("中国.icom.museum."))
965 .expect("failed to run lookup");
966
967 assert!(response.iter().next().is_some());
970 }
971
972 pub fn localhost_ipv4_test<E: Executor + Send + 'static, R: RuntimeProvider>(
974 mut exec: E,
975 handle: R::Handle,
976 ) {
977 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
978 ResolverConfig::default(),
979 ResolverOpts {
980 ip_strategy: LookupIpStrategy::Ipv4thenIpv6,
981 ..ResolverOpts::default()
982 },
983 handle,
984 )
985 .expect("failed to create resolver");
986
987 let response = exec
988 .block_on(resolver.lookup_ip("localhost"))
989 .expect("failed to run lookup");
990
991 let mut iter = response.iter();
992 assert_eq!(
993 iter.next().expect("no A"),
994 IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
995 );
996 }
997
998 pub fn localhost_ipv6_test<E: Executor + Send + 'static, R: RuntimeProvider>(
1000 mut exec: E,
1001 handle: R::Handle,
1002 ) {
1003 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
1004 ResolverConfig::default(),
1005 ResolverOpts {
1006 ip_strategy: LookupIpStrategy::Ipv6thenIpv4,
1007 ..ResolverOpts::default()
1008 },
1009 handle,
1010 )
1011 .expect("failed to create resolver");
1012
1013 let response = exec
1014 .block_on(resolver.lookup_ip("localhost"))
1015 .expect("failed to run lookup");
1016
1017 let mut iter = response.iter();
1018 assert_eq!(
1019 iter.next().expect("no AAAA"),
1020 IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1,))
1021 );
1022 }
1023
1024 pub fn search_ipv4_large_ndots_test<E: Executor + Send + 'static, R: RuntimeProvider>(
1026 mut exec: E,
1027 handle: R::Handle,
1028 ) {
1029 let mut config = ResolverConfig::default();
1030 config.add_search(Name::from_str("example.com").unwrap());
1031
1032 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
1033 config,
1034 ResolverOpts {
1035 ip_strategy: LookupIpStrategy::Ipv4Only,
1036 ndots: 5,
1037 ..ResolverOpts::default()
1038 },
1039 handle,
1040 )
1041 .expect("failed to create resolver");
1042
1043 let response = exec
1044 .block_on(resolver.lookup_ip("198.51.100.35"))
1045 .expect("failed to run lookup");
1046
1047 let mut iter = response.iter();
1048 assert_eq!(
1049 iter.next().expect("no rdatas"),
1050 IpAddr::V4(Ipv4Addr::new(198, 51, 100, 35))
1051 );
1052 }
1053
1054 pub fn search_ipv6_large_ndots_test<E: Executor + Send + 'static, R: RuntimeProvider>(
1056 mut exec: E,
1057 handle: R::Handle,
1058 ) {
1059 let mut config = ResolverConfig::default();
1060 config.add_search(Name::from_str("example.com").unwrap());
1061
1062 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
1063 config,
1064 ResolverOpts {
1065 ip_strategy: LookupIpStrategy::Ipv4Only,
1066 ndots: 5,
1067 ..ResolverOpts::default()
1068 },
1069 handle,
1070 )
1071 .expect("failed to create resolver");
1072
1073 let response = exec
1074 .block_on(resolver.lookup_ip("2001:db8::c633:6423"))
1075 .expect("failed to run lookup");
1076
1077 let mut iter = response.iter();
1078 assert_eq!(
1079 iter.next().expect("no rdatas"),
1080 IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc633, 0x6423))
1081 );
1082 }
1083
1084 pub fn search_ipv6_name_parse_fails_test<E: Executor + Send + 'static, R: RuntimeProvider>(
1086 mut exec: E,
1087 handle: R::Handle,
1088 ) {
1089 let mut config = ResolverConfig::default();
1090 config.add_search(Name::from_str("example.com").unwrap());
1091
1092 let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
1093 config,
1094 ResolverOpts {
1095 ip_strategy: LookupIpStrategy::Ipv4Only,
1096 ndots: 5,
1097 ..ResolverOpts::default()
1098 },
1099 handle,
1100 )
1101 .expect("failed to create resolver");
1102
1103 let response = exec
1104 .block_on(resolver.lookup_ip("2001:db8::198.51.100.35"))
1105 .expect("failed to run lookup");
1106
1107 let mut iter = response.iter();
1108 assert_eq!(
1109 iter.next().expect("no rdatas"),
1110 IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc633, 0x6423))
1111 );
1112 }
1113}
1114#[cfg(test)]
1115#[cfg(feature = "tokio-runtime")]
1116mod tests {
1117 use proto::xfer::DnsRequest;
1118 use tokio::runtime::Runtime;
1119
1120 use crate::config::{ResolverConfig, ResolverOpts};
1121 use crate::name_server::{TokioConnection, TokioConnectionProvider, TokioRuntime};
1122
1123 use super::*;
1124
1125 fn is_send_t<T: Send>() -> bool {
1126 true
1127 }
1128
1129 fn is_sync_t<T: Sync>() -> bool {
1130 true
1131 }
1132
1133 #[test]
1134 fn test_send_sync() {
1135 assert!(is_send_t::<ResolverConfig>());
1136 assert!(is_sync_t::<ResolverConfig>());
1137 assert!(is_send_t::<ResolverOpts>());
1138 assert!(is_sync_t::<ResolverOpts>());
1139
1140 assert!(is_send_t::<
1141 AsyncResolver<TokioConnection, TokioConnectionProvider>,
1142 >());
1143 assert!(is_sync_t::<
1144 AsyncResolver<TokioConnection, TokioConnectionProvider>,
1145 >());
1146
1147 assert!(is_send_t::<DnsRequest>());
1148 assert!(is_send_t::<LookupIpFuture<TokioConnection, ResolveError>>());
1149 assert!(is_send_t::<LookupFuture<TokioConnection, ResolveError>>());
1150 }
1151
1152 #[test]
1153 fn test_lookup_google() {
1154 use super::testing::lookup_test;
1155 let io_loop = Runtime::new().expect("failed to create tokio runtime");
1156 let handle = TokioHandle;
1157 lookup_test::<Runtime, TokioRuntime>(ResolverConfig::google(), io_loop, handle)
1158 }
1159
1160 #[test]
1161 fn test_lookup_cloudflare() {
1162 use super::testing::lookup_test;
1163 let io_loop = Runtime::new().expect("failed to create tokio runtime");
1164 let handle = TokioHandle;
1165 lookup_test::<Runtime, TokioRuntime>(ResolverConfig::cloudflare(), io_loop, handle)
1166 }
1167
1168 #[test]
1169 fn test_lookup_quad9() {
1170 use super::testing::lookup_test;
1171 let io_loop = Runtime::new().expect("failed to create tokio runtime");
1172 let handle = TokioHandle;
1173 lookup_test::<Runtime, TokioRuntime>(ResolverConfig::quad9(), io_loop, handle)
1174 }
1175
1176 #[test]
1177 fn test_ip_lookup() {
1178 use super::testing::ip_lookup_test;
1179 let io_loop = Runtime::new().expect("failed to create tokio runtime");
1180 let handle = TokioHandle;
1181 ip_lookup_test::<Runtime, TokioRuntime>(io_loop, handle)
1182 }
1183
1184 #[test]
1185 fn test_ip_lookup_across_threads() {
1186 use super::testing::ip_lookup_across_threads_test;
1187 let _io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1188 let handle = TokioHandle;
1189 ip_lookup_across_threads_test::<Runtime, TokioRuntime>(handle)
1190 }
1191
1192 #[test]
1193 #[cfg(feature = "dnssec")]
1194 fn test_sec_lookup() {
1195 use super::testing::sec_lookup_test;
1196 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1197 let handle = TokioHandle;
1198 sec_lookup_test::<Runtime, TokioRuntime>(io_loop, handle);
1199 }
1200
1201 #[test]
1202 #[cfg(feature = "dnssec")]
1203 fn test_sec_lookup_fails() {
1204 use super::testing::sec_lookup_fails_test;
1205 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1206 let handle = TokioHandle;
1207 sec_lookup_fails_test::<Runtime, TokioRuntime>(io_loop, handle);
1208 }
1209
1210 #[test]
1211 #[ignore]
1212 #[cfg(any(unix, target_os = "windows"))]
1213 #[cfg(feature = "system-config")]
1214 fn test_system_lookup() {
1215 use super::testing::system_lookup_test;
1216 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1217 let handle = TokioHandle;
1218 system_lookup_test::<Runtime, TokioRuntime>(io_loop, handle);
1219 }
1220
1221 #[test]
1222 #[ignore]
1223 #[cfg(unix)]
1225 fn test_hosts_lookup() {
1226 use super::testing::hosts_lookup_test;
1227 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1228 let handle = TokioHandle;
1229 hosts_lookup_test::<Runtime, TokioRuntime>(io_loop, handle);
1230 }
1231
1232 #[test]
1233 fn test_fqdn() {
1234 use super::testing::fqdn_test;
1235 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1236 let handle = TokioHandle;
1237 fqdn_test::<Runtime, TokioRuntime>(io_loop, handle);
1238 }
1239
1240 #[test]
1241 fn test_ndots() {
1242 use super::testing::ndots_test;
1243 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1244 let handle = TokioHandle;
1245 ndots_test::<Runtime, TokioRuntime>(io_loop, handle);
1246 }
1247
1248 #[test]
1249 fn test_large_ndots() {
1250 use super::testing::large_ndots_test;
1251 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1252 let handle = TokioHandle;
1253 large_ndots_test::<Runtime, TokioRuntime>(io_loop, handle);
1254 }
1255
1256 #[test]
1257 fn test_domain_search() {
1258 use super::testing::domain_search_test;
1259 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1260 let handle = TokioHandle;
1261 domain_search_test::<Runtime, TokioRuntime>(io_loop, handle);
1262 }
1263
1264 #[test]
1265 fn test_search_list() {
1266 use super::testing::search_list_test;
1267 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1268 let handle = TokioHandle;
1269 search_list_test::<Runtime, TokioRuntime>(io_loop, handle);
1270 }
1271
1272 #[test]
1273 fn test_idna() {
1274 use super::testing::idna_test;
1275 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1276 let handle = TokioHandle;
1277 idna_test::<Runtime, TokioRuntime>(io_loop, handle);
1278 }
1279
1280 #[test]
1281 fn test_localhost_ipv4() {
1282 use super::testing::localhost_ipv4_test;
1283 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1284 let handle = TokioHandle;
1285 localhost_ipv4_test::<Runtime, TokioRuntime>(io_loop, handle);
1286 }
1287
1288 #[test]
1289 fn test_localhost_ipv6() {
1290 use super::testing::localhost_ipv6_test;
1291 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1292 let handle = TokioHandle;
1293 localhost_ipv6_test::<Runtime, TokioRuntime>(io_loop, handle);
1294 }
1295
1296 #[test]
1297 fn test_search_ipv4_large_ndots() {
1298 use super::testing::search_ipv4_large_ndots_test;
1299 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1300 let handle = TokioHandle;
1301 search_ipv4_large_ndots_test::<Runtime, TokioRuntime>(io_loop, handle);
1302 }
1303
1304 #[test]
1305 fn test_search_ipv6_large_ndots() {
1306 use super::testing::search_ipv6_large_ndots_test;
1307 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1308 let handle = TokioHandle;
1309 search_ipv6_large_ndots_test::<Runtime, TokioRuntime>(io_loop, handle);
1310 }
1311
1312 #[test]
1313 fn test_search_ipv6_name_parse_fails() {
1314 use super::testing::search_ipv6_name_parse_fails_test;
1315 let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
1316 let handle = TokioHandle;
1317 search_ipv6_name_parse_fails_test::<Runtime, TokioRuntime>(io_loop, handle);
1318 }
1319
1320 #[test]
1321 fn test_build_names_onion() {
1322 let handle = TokioHandle;
1323 let mut config = ResolverConfig::default();
1324 config.add_search(Name::from_ascii("example.com.").unwrap());
1325 let resolver =
1326 AsyncResolver::<GenericConnection, GenericConnectionProvider<TokioRuntime>>::new(
1327 config,
1328 ResolverOpts::default(),
1329 handle,
1330 )
1331 .expect("failed to create resolver");
1332 let tor_address = [
1333 Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion")
1334 .unwrap(),
1335 Name::from_ascii("www.2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion")
1336 .unwrap(), ];
1338 let not_tor_address = [
1339 Name::from_ascii("onion").unwrap(),
1340 Name::from_ascii("www.onion").unwrap(),
1341 Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.www.onion")
1342 .unwrap(), Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion.to")
1344 .unwrap(), ];
1346 for name in &tor_address {
1347 assert_eq!(resolver.build_names(name.clone()).len(), 1);
1348 }
1349 for name in ¬_tor_address {
1350 assert_eq!(resolver.build_names(name.clone()).len(), 2);
1351 }
1352 }
1353}