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