1use async_trait::async_trait;
2use hickory_resolver::net::{DnsError as NetDnsError, NetError};
3use hickory_resolver::proto::dnssec::Proof;
4use hickory_resolver::proto::op::ResponseCode;
5use hickory_resolver::proto::rr::rdata::{A, AAAA, PTR, TXT};
6#[cfg(feature = "unbound")]
7use hickory_resolver::proto::rr::DNSClass;
8use hickory_resolver::proto::rr::{
9 LowerName, Name, RData, RecordData, RecordSet, RecordType, RrKey,
10};
11use hickory_resolver::proto::serialize::txt::Parser;
12use hickory_resolver::TokioResolver;
13#[cfg(feature = "unbound")]
14use libunbound::{AsyncContext, Context};
15use std::collections::{BTreeMap, BTreeSet};
16use std::fmt;
17use std::net::IpAddr;
18use std::str::FromStr;
19use std::time::{Duration, Instant};
20use thiserror::Error;
21
22pub(crate) const DEFAULT_NEGATIVE_TTL_SECS: u32 = 60;
24
25pub struct IpDisplay {
26 pub ip: IpAddr,
27 pub reverse: bool,
28}
29
30impl fmt::Display for IpDisplay {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self.ip {
33 IpAddr::V4(v4) => {
34 let mut bytes = v4.octets();
35 if self.reverse {
36 bytes.reverse();
37 }
38 let mut first = true;
39 for byte in bytes {
40 if !first {
41 f.write_str(".")?;
42 }
43 write!(f, "{byte}")?;
44 first = false;
45 }
46 Ok(())
47 }
48 IpAddr::V6(v6) => {
49 let mut bytes = v6.octets();
50 if self.reverse {
51 bytes.reverse();
52 }
53 let mut first = true;
54 for byte in bytes {
55 if !first {
56 f.write_str(".")?;
57 }
58 let (upper, lower) = (byte >> 4, byte & 0xf);
59 if self.reverse {
60 write!(f, "{lower:x}.{upper:x}")?;
61 } else {
62 write!(f, "{upper:x}.{lower:x}")?;
63 }
64 first = false;
65 }
66 Ok(())
67 }
68 }
69 }
70}
71
72pub fn ptr_host(ip: IpAddr) -> String {
73 let mut out = reverse_ip(ip);
74 out.push_str(match ip {
75 IpAddr::V4(_) => ".in-addr.arpa",
76 IpAddr::V6(_) => ".ip6.arpa",
77 });
78 out
79}
80
81pub fn reverse_ip(ip: IpAddr) -> String {
82 IpDisplay { ip, reverse: true }.to_string()
83}
84
85#[derive(Debug)]
86pub struct Answer {
87 pub canon_name: Option<String>,
88 pub records: Vec<RData>,
89 pub nxdomain: bool,
90 pub secure: bool,
91 pub bogus: bool,
92 pub why_bogus: Option<String>,
93 pub expires: Instant,
94 pub response_code: ResponseCode,
95}
96
97impl Answer {
98 pub fn as_txt(&self) -> Vec<String> {
99 let mut result = vec![];
100 for r in &self.records {
101 if let RData::TXT(txt) = r {
102 let mut joined = String::new();
103 for t in txt.txt_data.iter() {
104 joined.push_str(&String::from_utf8_lossy(t));
105 }
106 result.push(joined);
107 }
108 }
109 result
110 }
111
112 pub fn as_addr(&self) -> Vec<IpAddr> {
113 let mut result = vec![];
114 for r in &self.records {
115 match r {
116 RData::A(a) => result.push(a.0.into()),
117 RData::AAAA(a) => result.push(a.0.into()),
118 _ => {}
119 }
120 }
121 result
122 }
123}
124
125#[derive(Clone, Debug, Error, PartialEq)]
126pub enum DnsError {
127 #[error("invalid DNS name: {0}")]
128 InvalidName(String),
129 #[error("DNS: {0}")]
130 ResolveFailed(String),
131}
132
133impl DnsError {
134 pub(crate) fn from_resolve(name: &impl fmt::Display, err: NetError) -> Self {
135 DnsError::ResolveFailed(format!("failed to query DNS for {name}: {err}"))
136 }
137}
138
139fn no_records(err: &NetError) -> Option<&hickory_resolver::net::NoRecords> {
140 match err {
141 NetError::Dns(NetDnsError::NoRecordsFound(nr)) => Some(nr),
142 _ => None,
143 }
144}
145
146fn dnssec_status(proofs: impl Iterator<Item = Proof>) -> (bool, bool) {
154 let mut any = false;
155 let mut all_secure = true;
156 let mut bogus = false;
157 for proof in proofs {
158 any = true;
159 all_secure &= proof == Proof::Secure;
160 bogus |= proof == Proof::Bogus;
161 }
162 (any && all_secure, bogus)
163}
164
165#[async_trait]
166pub trait Resolver: Send + Sync + 'static {
167 async fn resolve_ip(&self, host: &str) -> Result<Vec<IpAddr>, DnsError>;
168
169 async fn resolve_mx(&self, host: &str) -> Result<Vec<Name>, DnsError>;
170
171 async fn resolve_ptr(&self, ip: IpAddr) -> Result<Vec<Name>, DnsError>;
172
173 async fn resolve_txt(&self, name: &str) -> Result<Answer, DnsError> {
174 let name = Name::from_str_relaxed(name)
175 .map_err(|err| DnsError::InvalidName(format!("invalid name {name}: {err}")))?;
176 self.resolve(name, RecordType::TXT).await
177 }
178
179 async fn resolve(&self, name: Name, rrtype: RecordType) -> Result<Answer, DnsError>;
180}
181
182#[async_trait]
183impl Resolver for Box<dyn Resolver> {
184 async fn resolve_ip(&self, host: &str) -> Result<Vec<IpAddr>, DnsError> {
185 (**self).resolve_ip(host).await
186 }
187
188 async fn resolve_mx(&self, host: &str) -> Result<Vec<Name>, DnsError> {
189 (**self).resolve_mx(host).await
190 }
191
192 async fn resolve_ptr(&self, ip: IpAddr) -> Result<Vec<Name>, DnsError> {
193 (**self).resolve_ptr(ip).await
194 }
195
196 async fn resolve_txt(&self, name: &str) -> Result<Answer, DnsError> {
197 (**self).resolve_txt(name).await
198 }
199
200 async fn resolve(&self, name: Name, rrtype: RecordType) -> Result<Answer, DnsError> {
201 (**self).resolve(name, rrtype).await
202 }
203}
204
205#[derive(Debug, Default)]
206struct TestZone {
207 records: BTreeMap<RrKey, RecordSet>,
208 secure: bool,
210}
211
212#[derive(Debug, Default)]
213pub struct TestResolver {
214 zones: BTreeMap<Name, TestZone>,
215 servfail: BTreeSet<Name>,
218 bogus: BTreeSet<Name>,
224}
225
226impl TestResolver {
227 pub fn with_zone(self, zone: &str) -> Result<Self, String> {
229 self.add_zone(zone, false)
230 }
231
232 pub fn with_secure_zone(self, zone: &str) -> Result<Self, String> {
234 self.add_zone(zone, true)
235 }
236
237 fn add_zone(mut self, zone: &str, secure: bool) -> Result<Self, String> {
238 let (mut name, records) = Parser::new(zone, None, None)
239 .parse()
240 .map_err(|err| format!("{err:#}"))?;
241 name.set_fqdn(true);
245 let fqdn_records = records
246 .into_iter()
247 .map(|(key, value)| {
248 if key.name().is_fqdn() {
249 (key, value)
250 } else {
251 let mut name: Name = key.name().into();
252 name.set_fqdn(true);
253 (RrKey::new(LowerName::new(&name), key.record_type), value)
254 }
255 })
256 .collect();
257 self.zones.insert(
258 name,
259 TestZone {
260 records: fqdn_records,
261 secure,
262 },
263 );
264 Ok(self)
265 }
266
267 pub fn with_servfail(mut self, name: &str) -> Self {
269 let mut name = Name::from_str_relaxed(name)
270 .expect("valid name passed to with_servfail")
271 .to_lowercase();
272 name.set_fqdn(true);
273 self.servfail.insert(name);
274 self
275 }
276
277 pub fn with_bogus(mut self, name: &str) -> Self {
281 let mut name = Name::from_str_relaxed(name)
282 .expect("valid name passed to with_bogus")
283 .to_lowercase();
284 name.set_fqdn(true);
285 self.bogus.insert(name);
286 self
287 }
288
289 pub fn with_txt(self, domain: &str, value: impl Into<String>) -> Self {
290 self.with_txt_multiple(domain, vec![value.into()])
291 }
292
293 pub fn with_txt_multiple(mut self, domain: &str, value: Vec<String>) -> Self {
295 let fqdn = format!("{}.", domain);
296 let authority = Name::from_str_relaxed(&fqdn).unwrap();
297 let key = RrKey {
298 name: LowerName::from_str(&fqdn).unwrap(),
299 record_type: RecordType::TXT,
300 };
301
302 let mut records = RecordSet::new(authority.clone(), RecordType::TXT, 0);
303 for item in value {
304 records.add_rdata(RData::TXT(TXT::new(vec![item])));
305 }
306 self.zones
307 .entry(authority)
308 .or_default()
309 .records
310 .insert(key, records);
311
312 self
313 }
314
315 fn get(&self, full: &Name, record_type: RecordType) -> Result<Answer, DnsError> {
316 let mut answer = self.get_impl(full, record_type)?;
317 let mut full_fqdn = full.clone();
318 full_fqdn.set_fqdn(true);
319 if self.bogus.contains(&full_fqdn.to_lowercase()) {
320 answer.bogus = true;
321 answer.why_bogus = Some("test bogus".to_string());
322 }
323 Ok(answer)
324 }
325
326 fn get_impl(&self, full: &Name, record_type: RecordType) -> Result<Answer, DnsError> {
327 let mut full_fqdn = full.clone();
328 full_fqdn.set_fqdn(true);
329
330 if self.servfail.contains(&full_fqdn.to_lowercase()) {
331 return Ok(Answer {
332 canon_name: None,
333 records: vec![],
334 nxdomain: false,
335 secure: false,
336 bogus: false,
337 why_bogus: None,
338 expires: Instant::now() + Duration::from_secs(60),
339 response_code: ResponseCode::ServFail,
340 });
341 }
342
343 let mut authority = full_fqdn.clone();
344
345 let zone = loop {
346 if let Some(zone) = self.zones.get(&authority) {
347 break zone;
348 };
349
350 if authority.num_labels() > 1 {
351 authority = authority.base_name();
352 continue;
353 }
354
355 return Ok(Answer {
356 canon_name: None,
357 records: vec![],
358 nxdomain: true,
359 secure: false,
360 bogus: false,
361 why_bogus: None,
362 expires: Instant::now() + Duration::from_secs(60),
363 response_code: ResponseCode::NXDomain,
364 });
365 };
366
367 let records = zone.records.get(&RrKey {
368 name: LowerName::from(&full_fqdn),
369 record_type,
370 });
371
372 let Some(records) = records else {
373 if record_type != RecordType::CNAME {
381 if let Some(cname_rs) = zone.records.get(&RrKey {
382 name: LowerName::from(&full_fqdn),
383 record_type: RecordType::CNAME,
384 }) {
385 if let Some(target) =
386 cname_rs
387 .records_without_rrsigs()
388 .find_map(|r| match &r.data {
389 RData::CNAME(cname) => Some(cname.0.clone()),
390 _ => None,
391 })
392 {
393 let mut target_answer = self.get(&target, record_type)?;
394 target_answer.canon_name = Some(target.to_ascii());
395 target_answer.secure = target_answer.secure && zone.secure;
396 return Ok(target_answer);
397 }
398 }
399 }
400
401 return Ok(Answer {
402 canon_name: None,
403 records: vec![],
404 nxdomain: false,
405 secure: zone.secure,
406 bogus: false,
407 why_bogus: None,
408 expires: Instant::now() + Duration::from_secs(60),
409 response_code: ResponseCode::NoError,
410 });
411 };
412
413 Ok(Answer {
414 canon_name: None,
415 records: records
416 .records_without_rrsigs()
417 .map(|r| r.data.clone())
418 .collect(),
419 nxdomain: false,
420 secure: zone.secure,
421 bogus: false,
422 why_bogus: None,
423 expires: Instant::now() + Duration::from_secs(60),
424 response_code: ResponseCode::NoError,
425 })
426 }
427}
428
429#[async_trait]
430impl Resolver for TestResolver {
431 async fn resolve_ip(&self, full: &str) -> Result<Vec<IpAddr>, DnsError> {
432 let name = Name::from_str_relaxed(full)
433 .map_err(|err| DnsError::InvalidName(format!("invalid name {full}: {err}")))?;
434
435 let mut values = vec![];
436 let answer = self.get(&name, RecordType::A)?;
437 for record in answer.records {
438 let a = A::try_borrow(&record).unwrap();
439 values.push(IpAddr::V4(a.0));
440 }
441
442 let answer = self.get(&name, RecordType::AAAA)?;
443 for record in answer.records {
444 let a = AAAA::try_borrow(&record).unwrap();
445 values.push(IpAddr::V6(a.0));
446 }
447
448 Ok(values)
449 }
450
451 async fn resolve_mx(&self, full: &str) -> Result<Vec<Name>, DnsError> {
452 let name = Name::from_str_relaxed(full)
453 .map_err(|err| DnsError::InvalidName(format!("invalid name {full}: {err}")))?;
454
455 let mut values = vec![];
456 let answer = self.get(&name, RecordType::MX)?;
457 for record in answer.records {
458 if let RData::MX(mx) = record {
459 values.push(mx.exchange.clone());
460 }
461 }
462
463 Ok(values)
464 }
465
466 async fn resolve_ptr(&self, ip: IpAddr) -> Result<Vec<Name>, DnsError> {
467 let name = ptr_host(ip);
468 let name = Name::from_str_relaxed(ptr_host(ip))
469 .map_err(|err| DnsError::InvalidName(format!("invalid name {name}: {err}")))?;
470
471 let mut values = vec![];
472 let answer = self.get(&name, RecordType::PTR)?;
473 for record in answer.records {
474 match PTR::try_borrow(&record) {
475 Some(ptr) => values.push(ptr.0.clone()),
476 None => {
477 println!("invalid record found for PTR record for {ip}");
478 return Err(DnsError::ResolveFailed(format!(
479 "invalid record found for PTR record for {ip}"
480 )));
481 }
482 };
483 }
484
485 Ok(values)
486 }
487
488 async fn resolve(&self, name: Name, rrtype: RecordType) -> Result<Answer, DnsError> {
489 self.get(&name, rrtype)
490 }
491}
492
493#[cfg(feature = "unbound")]
494pub struct UnboundResolver {
495 cx: AsyncContext,
496}
497
498#[cfg(feature = "unbound")]
499impl UnboundResolver {
500 pub fn new() -> Result<Self, libunbound::Error> {
501 let context = Context::new()?;
503 context.add_builtin_trust_anchors()?;
505 Ok(Self {
506 cx: context.into_async()?,
507 })
508 }
509}
510
511#[cfg(feature = "unbound")]
512#[async_trait]
513impl Resolver for UnboundResolver {
514 async fn resolve_ip(&self, host: &str) -> Result<Vec<IpAddr>, DnsError> {
515 let (a, aaaa) = tokio::join!(
516 self.cx.resolve(host, RecordType::A, DNSClass::IN),
517 self.cx.resolve(host, RecordType::AAAA, DNSClass::IN),
518 );
519
520 let mut records = vec![];
521 match (a, aaaa) {
522 (Ok(a), Ok(aaaa)) => {
523 records.extend(a.rdata().filter_map(|r| match r {
524 Ok(RData::A(a)) => Some(IpAddr::from(a.0)),
525 _ => None,
526 }));
527 records.extend(aaaa.rdata().filter_map(|r| match r {
528 Ok(RData::AAAA(aaaa)) => Some(IpAddr::from(aaaa.0)),
529 _ => None,
530 }));
531 }
532 (Ok(a), Err(_)) => {
533 records.extend(a.rdata().filter_map(|r| match r {
534 Ok(RData::A(a)) => Some(IpAddr::from(a.0)),
535 _ => None,
536 }));
537 }
538 (Err(_), Ok(aaaa)) => {
539 records.extend(aaaa.rdata().filter_map(|r| match r {
540 Ok(RData::AAAA(aaaa)) => Some(IpAddr::from(aaaa.0)),
541 _ => None,
542 }));
543 }
544 (Err(err), Err(_)) => {
545 return Err(DnsError::ResolveFailed(format!(
546 "failed to query DNS for {host}: {err}"
547 )))
548 }
549 }
550
551 Ok(records)
552 }
553
554 async fn resolve_mx(&self, host: &str) -> Result<Vec<Name>, DnsError> {
555 let answer = self
556 .cx
557 .resolve(host, RecordType::A, DNSClass::IN)
558 .await
559 .map_err(|err| {
560 DnsError::ResolveFailed(format!("failed to query DNS for {host}: {err}"))
561 })?;
562
563 Ok(answer
564 .rdata()
565 .filter_map(|r| match r {
566 Ok(RData::MX(mx)) => Some(mx.exchange.clone()),
567 _ => None,
568 })
569 .collect())
570 }
571
572 async fn resolve_ptr(&self, ip: IpAddr) -> Result<Vec<Name>, DnsError> {
573 let name = ptr_host(ip);
574 let answer = self
575 .cx
576 .resolve(&name, RecordType::PTR, DNSClass::IN)
577 .await
578 .map_err(|err| {
579 DnsError::ResolveFailed(format!("failed to query DNS for {name}: {err}"))
580 })?;
581
582 Ok(answer
583 .rdata()
584 .filter_map(|r| match r {
585 Ok(RData::PTR(ptr)) => Some(ptr.0.clone()),
586 _ => None,
587 })
588 .collect())
589 }
590
591 async fn resolve(&self, name: Name, rrtype: RecordType) -> Result<Answer, DnsError> {
592 let name = name.to_ascii();
593 let answer = self
594 .cx
595 .resolve(&name, rrtype, DNSClass::IN)
596 .await
597 .map_err(|err| {
598 DnsError::ResolveFailed(format!("failed to query DNS for {name}: {err}"))
599 })?;
600
601 let mut records = vec![];
602 for r in answer.rdata() {
603 if let Ok(r) = r {
604 records.push(r);
605 }
606 }
607
608 Ok(Answer {
609 canon_name: answer.canon_name().map(|s| s.to_string()),
610 records,
611 nxdomain: answer.nxdomain(),
612 secure: answer.secure(),
613 bogus: answer.bogus(),
614 why_bogus: answer.why_bogus().map(|s| s.to_string()),
615 response_code: answer.rcode(),
616 expires: Instant::now() + Duration::from_secs(answer.ttl() as u64),
617 })
618 }
619}
620
621#[cfg(feature = "unbound")]
622impl From<AsyncContext> for UnboundResolver {
623 fn from(cx: AsyncContext) -> Self {
624 Self { cx }
625 }
626}
627
628pub struct HickoryResolver {
629 inner: TokioResolver,
630}
631
632impl HickoryResolver {
633 pub fn new() -> Result<Self, NetError> {
634 Ok(Self {
635 inner: TokioResolver::builder_tokio()?.build()?,
636 })
637 }
638}
639
640#[async_trait]
641impl Resolver for HickoryResolver {
642 async fn resolve_ip(&self, host: &str) -> Result<Vec<IpAddr>, DnsError> {
643 let name = Name::from_str_relaxed(host)
644 .map_err(|err| DnsError::InvalidName(format!("invalid name {host}: {err}")))?;
645
646 match self.inner.lookup_ip(name.clone()).await {
647 Ok(result) => Ok(result.iter().collect()),
648 Err(err) => match no_records(&err) {
649 Some(_) => Ok(vec![]),
650 None => Err(DnsError::from_resolve(&name, err)),
651 },
652 }
653 }
654
655 async fn resolve_mx(&self, host: &str) -> Result<Vec<Name>, DnsError> {
656 let name = Name::from_str_relaxed(host)
657 .map_err(|err| DnsError::InvalidName(format!("invalid name {host}: {err}")))?;
658
659 match self.inner.mx_lookup(name.clone()).await {
660 Ok(result) => Ok(result
661 .answers()
662 .iter()
663 .filter_map(|r| match &r.data {
664 RData::MX(mx) => Some(mx.exchange.clone()),
665 _ => None,
666 })
667 .collect()),
668 Err(err) => match no_records(&err) {
669 Some(_) => Ok(vec![]),
670 None => Err(DnsError::from_resolve(&name, err)),
671 },
672 }
673 }
674
675 async fn resolve_ptr(&self, ip: IpAddr) -> Result<Vec<Name>, DnsError> {
676 match self.inner.reverse_lookup(ip).await {
677 Ok(result) => Ok(result
678 .answers()
679 .iter()
680 .filter_map(|r| match &r.data {
681 RData::PTR(ptr) => Some(ptr.0.clone()),
682 _ => None,
683 })
684 .collect()),
685 Err(err) => match no_records(&err) {
686 Some(_) => Ok(vec![]),
687 None => Err(DnsError::from_resolve(&ip, err)),
688 },
689 }
690 }
691
692 async fn resolve(&self, name: Name, rrtype: RecordType) -> Result<Answer, DnsError> {
693 match self.inner.lookup(name.clone(), rrtype).await {
694 Ok(result) => {
695 let expires = result.valid_until();
696 let answers = result.answers();
697 let (secure, bogus) = dnssec_status(answers.iter().map(|r| r.proof));
700 let records = answers.iter().map(|r| r.data.clone()).collect();
701 Ok(Answer {
702 canon_name: None,
703 records,
704 nxdomain: false,
705 secure,
706 bogus,
707 why_bogus: None,
708 expires,
709 response_code: ResponseCode::NoError,
710 })
711 }
712 Err(err) => match no_records(&err) {
713 Some(no_records) => {
714 let authorities = no_records.authorities.as_deref().unwrap_or(&[]);
721 let (secure, bogus) = dnssec_status(authorities.iter().map(|r| r.proof));
722 Ok(Answer {
723 canon_name: None,
724 records: vec![],
725 nxdomain: no_records.response_code == ResponseCode::NXDomain,
726 secure,
727 bogus,
728 why_bogus: None,
729 response_code: no_records.response_code,
730 expires: Instant::now()
731 + Duration::from_secs(
732 no_records.negative_ttl.unwrap_or(DEFAULT_NEGATIVE_TTL_SECS) as u64,
733 ),
734 })
735 }
736 None => Err(DnsError::from_resolve(&name, err)),
737 },
738 }
739 }
740}
741
742impl From<TokioResolver> for HickoryResolver {
743 fn from(inner: TokioResolver) -> Self {
744 Self { inner }
745 }
746}
747
748pub struct AggregateResolver {
756 resolvers: Vec<Box<dyn Resolver>>,
757}
758
759impl AggregateResolver {
760 pub fn new() -> Self {
761 Self { resolvers: vec![] }
762 }
763
764 pub fn push_resolver(&mut self, resolver: Box<dyn Resolver>) {
765 self.resolvers.push(resolver);
766 }
767}
768
769#[async_trait]
770impl Resolver for AggregateResolver {
771 async fn resolve_ip(&self, full: &str) -> Result<Vec<IpAddr>, DnsError> {
772 let mut errors = vec![];
773 for resolver in &self.resolvers {
774 match resolver.resolve_ip(full).await {
775 Ok(ips) if ips.is_empty() => {}
776 Ok(ips) => {
777 return Ok(ips);
778 }
779 Err(err) => errors.push(err),
780 }
781 }
782
783 if let Some(err) = errors.pop() {
784 Err(err)
785 } else {
786 Ok(vec![])
787 }
788 }
789
790 async fn resolve_mx(&self, full: &str) -> Result<Vec<Name>, DnsError> {
791 let mut errors = vec![];
792 for resolver in &self.resolvers {
793 match resolver.resolve_mx(full).await {
794 Ok(ips) if ips.is_empty() => {}
795 Ok(ips) => {
796 return Ok(ips);
797 }
798 Err(err) => errors.push(err),
799 }
800 }
801
802 if let Some(err) = errors.pop() {
803 Err(err)
804 } else {
805 Ok(vec![])
806 }
807 }
808
809 async fn resolve_ptr(&self, ip: IpAddr) -> Result<Vec<Name>, DnsError> {
810 let mut errors = vec![];
811 for resolver in &self.resolvers {
812 match resolver.resolve_ptr(ip).await {
813 Ok(ips) if ips.is_empty() => {}
814 Ok(ips) => {
815 return Ok(ips);
816 }
817 Err(err) => errors.push(err),
818 }
819 }
820
821 if let Some(err) = errors.pop() {
822 Err(err)
823 } else {
824 Ok(vec![])
825 }
826 }
827
828 async fn resolve(&self, name: Name, rrtype: RecordType) -> Result<Answer, DnsError> {
829 let mut errors = vec![];
830 let mut nxdomain = None;
831 for resolver in &self.resolvers {
832 match resolver.resolve(name.clone(), rrtype).await {
833 Ok(answer) => {
834 if !answer.nxdomain {
835 return Ok(answer);
836 }
837 nxdomain.replace(answer);
838 }
839 Err(err) => errors.push(err),
840 }
841 }
842
843 if let Some(err) = errors.pop() {
844 Err(err)
845 } else {
846 match nxdomain.take() {
847 Some(answer) => Ok(answer),
848 None => Ok(Answer {
849 canon_name: None,
850 records: vec![],
851 nxdomain: true,
852 secure: false,
853 bogus: false,
854 why_bogus: None,
855 expires: Instant::now() + Duration::from_secs(60),
856 response_code: ResponseCode::NXDomain,
857 }),
858 }
859 }
860 }
861}
862
863#[cfg(test)]
864mod tests {
865 use super::ptr_host;
866 use std::net::{Ipv4Addr, Ipv6Addr};
867 use std::str::FromStr;
868
869 #[test]
870 fn test_ptr_host() {
871 assert_eq!(
872 ptr_host(Ipv4Addr::new(192, 0, 2, 1).into()),
873 "1.2.0.192.in-addr.arpa"
874 );
875 assert_eq!(
876 ptr_host(Ipv6Addr::from_str("2001:db8::1").unwrap().into()),
877 "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"
878 );
879 }
880}