hickory_resolver/
error.rs1use std::{fmt, io, sync};
11
12use thiserror::Error;
13
14#[cfg(feature = "backtrace")]
15use crate::proto::{ExtBacktrace, trace};
16use crate::proto::{
17 ProtoError, ProtoErrorKind,
18 rr::{Record, rdata::SOA},
19 xfer::retry_dns_handle::RetryableError,
20};
21
22#[allow(clippy::large_enum_variant)]
23#[derive(Debug, Error)]
25#[non_exhaustive]
26pub enum ResolveErrorKind {
27 #[error("{0}")]
29 Message(&'static str),
30
31 #[error("{0}")]
33 Msg(String),
34
35 #[error("proto error: {0}")]
37 Proto(#[from] ProtoError),
38}
39
40impl Clone for ResolveErrorKind {
41 fn clone(&self) -> Self {
42 use self::ResolveErrorKind::*;
43 match self {
44 Message(msg) => Message(msg),
45 Msg(msg) => Msg(msg.clone()),
46 Proto(proto) => Self::from(proto.clone()),
48 }
49 }
50}
51
52#[derive(Debug, Clone, Error)]
54pub struct ResolveError {
55 pub(crate) kind: ResolveErrorKind,
56 #[cfg(feature = "backtrace")]
57 backtrack: Option<ExtBacktrace>,
58}
59
60impl ResolveError {
61 pub fn kind(&self) -> &ResolveErrorKind {
63 &self.kind
64 }
65
66 pub fn into_kind(self) -> ResolveErrorKind {
68 self.kind
69 }
70
71 pub fn proto(&self) -> Option<&ProtoError> {
73 match &self.kind {
74 ResolveErrorKind::Proto(proto) => Some(proto),
75 _ => None,
76 }
77 }
78
79 pub fn is_nx_domain(&self) -> bool {
81 self.proto()
82 .map(|proto| proto.is_nx_domain())
83 .unwrap_or(false)
84 }
85
86 pub fn is_no_records_found(&self) -> bool {
88 self.proto()
89 .map(|proto| proto.is_no_records_found())
90 .unwrap_or(false)
91 }
92
93 pub fn into_soa(self) -> Option<Box<Record<SOA>>> {
95 match self.kind {
96 ResolveErrorKind::Proto(proto) => proto.into_soa(),
97 _ => None,
98 }
99 }
100}
101
102impl RetryableError for ResolveError {
103 fn should_retry(&self) -> bool {
104 match self.kind() {
105 ResolveErrorKind::Message(_) | ResolveErrorKind::Msg(_) => false,
106 ResolveErrorKind::Proto(proto) => proto.should_retry(),
107 }
108 }
109
110 fn attempted(&self) -> bool {
111 match self.kind() {
112 ResolveErrorKind::Proto(e) => e.attempted(),
113 _ => true,
114 }
115 }
116}
117
118impl fmt::Display for ResolveError {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 cfg_if::cfg_if! {
121 if #[cfg(feature = "backtrace")] {
122 if let Some(backtrace) = &self.backtrack {
123 fmt::Display::fmt(&self.kind, f)?;
124 fmt::Debug::fmt(backtrace, f)
125 } else {
126 fmt::Display::fmt(&self.kind, f)
127 }
128 } else {
129 fmt::Display::fmt(&self.kind, f)
130 }
131 }
132 }
133}
134
135impl From<ResolveErrorKind> for ResolveError {
136 fn from(kind: ResolveErrorKind) -> Self {
137 Self {
138 kind,
139 #[cfg(feature = "backtrace")]
140 backtrack: trace!(),
141 }
142 }
143}
144
145impl From<&'static str> for ResolveError {
146 fn from(msg: &'static str) -> Self {
147 ResolveErrorKind::Message(msg).into()
148 }
149}
150
151impl TryFrom<ResolveError> for ProtoErrorKind {
152 type Error = ResolveError;
153 fn try_from(error: ResolveError) -> Result<Self, Self::Error> {
154 match error.kind {
155 ResolveErrorKind::Proto(p) => Ok(*p.kind),
156 _ => Err(error),
157 }
158 }
159}
160
161#[cfg(target_os = "windows")]
162#[cfg(feature = "system-config")]
163impl From<ipconfig::error::Error> for ResolveError {
164 fn from(e: ipconfig::error::Error) -> ResolveError {
165 ResolveErrorKind::Msg(format!("failed to read from registry: {}", e)).into()
166 }
167}
168
169impl From<String> for ResolveError {
170 fn from(msg: String) -> Self {
171 ResolveErrorKind::Msg(msg).into()
172 }
173}
174
175impl From<io::Error> for ResolveError {
176 fn from(e: io::Error) -> Self {
177 ResolveErrorKind::from(ProtoError::from(e)).into()
178 }
179}
180
181impl From<ProtoError> for ResolveError {
182 fn from(e: ProtoError) -> Self {
183 ResolveErrorKind::Proto(e).into()
184 }
185}
186
187impl From<ResolveError> for io::Error {
188 fn from(e: ResolveError) -> Self {
189 Self::new(io::ErrorKind::Other, e)
190 }
191}
192
193impl<T> From<sync::PoisonError<T>> for ResolveError {
194 fn from(e: sync::PoisonError<T>) -> Self {
195 ResolveErrorKind::Msg(format!("lock was poisoned, this is non-recoverable: {e}")).into()
196 }
197}