1#![deny(missing_docs)]
11
12use std::{fmt, io, sync};
13
14#[cfg(not(feature = "openssl"))]
15use self::not_openssl::SslErrorStack;
16#[cfg(not(feature = "ring"))]
17use self::not_ring::Unspecified;
18#[cfg(feature = "backtrace")]
19#[cfg_attr(docsrs, doc(cfg(feature = "backtrace")))]
20pub use backtrace::Backtrace as ExtBacktrace;
21use enum_as_inner::EnumAsInner;
22#[cfg(feature = "backtrace")]
23use lazy_static::lazy_static;
24#[cfg(feature = "openssl")]
25use openssl::error::ErrorStack as SslErrorStack;
26#[cfg(feature = "ring")]
27use ring::error::Unspecified;
28use thiserror::Error;
29
30use crate::op::Header;
31use crate::rr::{Name, RecordType};
32use crate::serialize::binary::DecodeError;
33
34#[cfg(feature = "backtrace")]
35#[cfg_attr(docsrs, doc(cfg(feature = "backtrace")))]
36lazy_static! {
37 pub static ref ENABLE_BACKTRACE: bool = {
39 use std::env;
40 let bt = env::var("RUST_BACKTRACE");
41 matches!(bt.as_ref().map(|s| s as &str), Ok("full") | Ok("1"))
42 };
43}
44
45#[cfg(feature = "backtrace")]
49#[cfg_attr(docsrs, doc(cfg(feature = "backtrace")))]
50#[macro_export]
51macro_rules! trace {
52 () => {{
53 use $crate::error::ExtBacktrace as Backtrace;
54
55 if *$crate::error::ENABLE_BACKTRACE {
56 Some(Backtrace::new())
57 } else {
58 None
59 }
60 }};
61}
62
63pub type ProtoResult<T> = ::std::result::Result<T, ProtoError>;
65
66#[derive(Debug, EnumAsInner, Error)]
68#[non_exhaustive]
69pub enum ProtoErrorKind {
70 #[error("there should only be one query per request, got: {0}")]
72 BadQueryCount(usize),
73
74 #[error("resource too busy")]
80 Busy,
81
82 #[error("future was canceled: {0:?}")]
84 Canceled(futures_channel::oneshot::Canceled),
85
86 #[error("char data length exceeds {max}: {len}")]
88 CharacterDataTooLong {
89 max: usize,
91 len: usize,
93 },
94
95 #[error("overlapping labels name {label} other {other}")]
97 LabelOverlapsWithOther {
98 label: usize,
100 other: usize,
102 },
103
104 #[error("dns key value unknown, must be 3: {0}")]
106 DnsKeyProtocolNot3(u8),
107
108 #[error("name label data exceed 255: {0}")]
110 DomainNameTooLong(usize),
111
112 #[error("edns resource record label must be the root label (.): {0}")]
114 EdnsNameNotRoot(crate::rr::Name),
115
116 #[error("message format error: {error}")]
118 FormError {
119 header: Header,
121 error: Box<ProtoError>,
123 },
124
125 #[error("hmac validation failure")]
127 HmacInvalid(),
128
129 #[error("incorrect rdata length read: {read} expected: {len}")]
131 IncorrectRDataLengthRead {
132 read: usize,
134 len: usize,
136 },
137
138 #[error("label bytes exceed 63: {0}")]
140 LabelBytesTooLong(usize),
141
142 #[error("label points to data not prior to idx: {idx} ptr: {ptr}")]
144 PointerNotPriorToLabel {
145 idx: usize,
147 ptr: u16,
149 },
150
151 #[error("maximum buffer size exceeded: {0}")]
153 MaxBufferSizeExceeded(usize),
154
155 #[error("{0}")]
157 Message(&'static str),
158
159 #[error("{0}")]
161 Msg(String),
162
163 #[error("no error specified")]
165 NoError,
166
167 #[error("not all records could be written, wrote: {count}")]
169 NotAllRecordsWritten {
170 count: usize,
172 },
173
174 #[error("rrsigs are not present for record set name: {name} record_type: {record_type}")]
176 RrsigsNotPresent {
177 name: Name,
179 record_type: RecordType,
181 },
182
183 #[error("algorithm type value unknown: {0}")]
185 UnknownAlgorithmTypeValue(u8),
186
187 #[error("dns class string unknown: {0}")]
189 UnknownDnsClassStr(String),
190
191 #[error("dns class value unknown: {0}")]
193 UnknownDnsClassValue(u16),
194
195 #[error("record type string unknown: {0}")]
197 UnknownRecordTypeStr(String),
198
199 #[error("record type value unknown: {0}")]
201 UnknownRecordTypeValue(u16),
202
203 #[error("unrecognized label code: {0:b}")]
205 UnrecognizedLabelCode(u8),
206
207 #[error("nsec3 flags should be 0b0000000*: {0:b}")]
209 UnrecognizedNsec3Flags(u8),
210
211 #[error("csync flags should be 0b000000**: {0:b}")]
213 UnrecognizedCsyncFlags(u16),
214
215 #[error("io error: {0}")]
218 Io(io::Error),
219
220 #[error("lock poisoned error")]
222 Poisoned,
223
224 #[error("ring error: {0}")]
226 Ring(#[from] Unspecified),
227
228 #[error("ssl error: {0}")]
230 SSL(#[from] SslErrorStack),
231
232 #[error("timer error")]
234 Timer,
235
236 #[error("request timed out")]
238 Timeout,
239
240 #[error("url parsing error")]
242 UrlParsing(#[from] url::ParseError),
243
244 #[error("error parsing utf8 string")]
246 Utf8(#[from] std::str::Utf8Error),
247
248 #[error("error parsing utf8 string")]
250 FromUtf8(#[from] std::string::FromUtf8Error),
251
252 #[error("error parsing int")]
254 ParseInt(#[from] std::num::ParseIntError),
255
256 #[cfg(feature = "quinn")]
258 #[error("error creating quic connection: {0}")]
259 QuinnConnect(#[from] quinn::ConnectError),
260
261 #[cfg(feature = "quinn")]
263 #[error("error with quic connection: {0}")]
264 QuinnConnection(#[from] quinn::ConnectionError),
265
266 #[cfg(feature = "quinn")]
268 #[error("error writing to quic connection: {0}")]
269 QuinnWriteError(#[from] quinn::WriteError),
270
271 #[cfg(feature = "quinn")]
273 #[error("error writing to quic read: {0}")]
274 QuinnReadError(#[from] quinn::ReadExactError),
275
276 #[cfg(feature = "quinn")]
278 #[error("error constructing quic configuration: {0}")]
279 QuinnConfigError(#[from] quinn::ConfigError),
280
281 #[cfg(feature = "quinn")]
283 #[error("an unknown quic stream was used")]
284 QuinnUnknownStreamError,
285
286 #[cfg(feature = "quinn")]
288 #[error("quic messages should always be 0, got: {0}")]
289 QuicMessageIdNot0(u16),
290
291 #[cfg(feature = "rustls")]
293 #[error("rustls construction error: {0}")]
294 RustlsError(#[from] rustls::Error),
295}
296
297#[derive(Error, Clone, Debug)]
299#[non_exhaustive]
300pub struct ProtoError {
301 pub kind: Box<ProtoErrorKind>,
303 #[cfg(feature = "backtrace")]
305 pub backtrack: Option<ExtBacktrace>,
306}
307
308impl ProtoError {
309 pub fn kind(&self) -> &ProtoErrorKind {
311 &self.kind
312 }
313
314 pub fn is_busy(&self) -> bool {
316 matches!(*self.kind, ProtoErrorKind::Busy)
317 }
318}
319
320impl fmt::Display for ProtoError {
321 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322 cfg_if::cfg_if! {
323 if #[cfg(feature = "backtrace")] {
324 if let Some(ref backtrace) = self.backtrack {
325 fmt::Display::fmt(&self.kind, f)?;
326 fmt::Debug::fmt(backtrace, f)
327 } else {
328 fmt::Display::fmt(&self.kind, f)
329 }
330 } else {
331 fmt::Display::fmt(&self.kind, f)
332 }
333 }
334 }
335}
336
337impl<E> From<E> for ProtoError
338where
339 E: Into<ProtoErrorKind>,
340{
341 fn from(error: E) -> Self {
342 let kind: ProtoErrorKind = error.into();
343
344 Self {
345 kind: Box::new(kind),
346 #[cfg(feature = "backtrace")]
347 backtrack: trace!(),
348 }
349 }
350}
351
352impl From<DecodeError> for ProtoError {
353 fn from(err: DecodeError) -> Self {
354 match err {
355 DecodeError::PointerNotPriorToLabel { idx, ptr } => {
356 ProtoErrorKind::PointerNotPriorToLabel { idx, ptr }
357 }
358 DecodeError::LabelBytesTooLong(len) => ProtoErrorKind::LabelBytesTooLong(len),
359 DecodeError::UnrecognizedLabelCode(code) => ProtoErrorKind::UnrecognizedLabelCode(code),
360 DecodeError::DomainNameTooLong(len) => ProtoErrorKind::DomainNameTooLong(len),
361 DecodeError::LabelOverlapsWithOther { label, other } => {
362 ProtoErrorKind::LabelOverlapsWithOther { label, other }
363 }
364 _ => ProtoErrorKind::Msg(err.to_string()),
365 }
366 .into()
367 }
368}
369
370impl From<&'static str> for ProtoError {
371 fn from(msg: &'static str) -> Self {
372 ProtoErrorKind::Message(msg).into()
373 }
374}
375
376impl From<String> for ProtoError {
377 fn from(msg: String) -> Self {
378 ProtoErrorKind::Msg(msg).into()
379 }
380}
381
382impl From<io::Error> for ProtoErrorKind {
383 fn from(e: io::Error) -> Self {
384 match e.kind() {
385 io::ErrorKind::TimedOut => Self::Timeout,
386 _ => Self::Io(e),
387 }
388 }
389}
390
391impl<T> From<sync::PoisonError<T>> for ProtoError {
392 fn from(_e: sync::PoisonError<T>) -> Self {
393 ProtoErrorKind::Poisoned.into()
394 }
395}
396
397#[cfg(not(feature = "openssl"))]
399#[cfg_attr(docsrs, doc(cfg(not(feature = "openssl"))))]
400pub mod not_openssl {
401 use std;
402
403 #[derive(Clone, Copy, Debug)]
405 pub struct SslErrorStack;
406
407 impl std::fmt::Display for SslErrorStack {
408 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
409 Ok(())
410 }
411 }
412
413 impl std::error::Error for SslErrorStack {
414 fn description(&self) -> &str {
415 "openssl feature not enabled"
416 }
417 }
418}
419
420#[cfg(not(feature = "ring"))]
422#[cfg_attr(docsrs, doc(cfg(not(feature = "ring"))))]
423pub mod not_ring {
424 use std;
425
426 #[derive(Clone, Copy, Debug)]
428 pub struct Unspecified;
429
430 impl std::fmt::Display for Unspecified {
431 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
432 Ok(())
433 }
434 }
435
436 impl std::error::Error for Unspecified {
437 fn description(&self) -> &str {
438 "ring feature not enabled"
439 }
440 }
441}
442
443impl From<ProtoError> for io::Error {
444 fn from(e: ProtoError) -> Self {
445 match *e.kind() {
446 ProtoErrorKind::Timeout => Self::new(io::ErrorKind::TimedOut, e),
447 _ => Self::new(io::ErrorKind::Other, e),
448 }
449 }
450}
451
452impl From<ProtoError> for String {
453 fn from(e: ProtoError) -> Self {
454 e.to_string()
455 }
456}
457
458#[cfg(feature = "wasm-bindgen")]
459#[cfg_attr(docsrs, doc(cfg(feature = "wasm-bindgen")))]
460impl From<ProtoError> for wasm_bindgen_crate::JsValue {
461 fn from(e: ProtoError) -> Self {
462 js_sys::Error::new(&e.to_string()).into()
463 }
464}
465
466impl Clone for ProtoErrorKind {
467 fn clone(&self) -> Self {
468 use self::ProtoErrorKind::*;
469 match *self {
470 BadQueryCount(count) => BadQueryCount(count),
471 Busy => Busy,
472 Canceled(ref c) => Canceled(*c),
473 CharacterDataTooLong { max, len } => CharacterDataTooLong { max, len },
474 LabelOverlapsWithOther { label, other } => LabelOverlapsWithOther { label, other },
475 DnsKeyProtocolNot3(protocol) => DnsKeyProtocolNot3(protocol),
476 DomainNameTooLong(len) => DomainNameTooLong(len),
477 EdnsNameNotRoot(ref found) => EdnsNameNotRoot(found.clone()),
478 FormError { header, ref error } => FormError {
479 header,
480 error: error.clone(),
481 },
482 HmacInvalid() => HmacInvalid(),
483 IncorrectRDataLengthRead { read, len } => IncorrectRDataLengthRead { read, len },
484 LabelBytesTooLong(len) => LabelBytesTooLong(len),
485 PointerNotPriorToLabel { idx, ptr } => PointerNotPriorToLabel { idx, ptr },
486 MaxBufferSizeExceeded(max) => MaxBufferSizeExceeded(max),
487 Message(msg) => Message(msg),
488 Msg(ref msg) => Msg(msg.clone()),
489 NoError => NoError,
490 NotAllRecordsWritten { count } => NotAllRecordsWritten { count },
491 RrsigsNotPresent {
492 ref name,
493 ref record_type,
494 } => RrsigsNotPresent {
495 name: name.clone(),
496 record_type: *record_type,
497 },
498 UnknownAlgorithmTypeValue(value) => UnknownAlgorithmTypeValue(value),
499 UnknownDnsClassStr(ref value) => UnknownDnsClassStr(value.clone()),
500 UnknownDnsClassValue(value) => UnknownDnsClassValue(value),
501 UnknownRecordTypeStr(ref value) => UnknownRecordTypeStr(value.clone()),
502 UnknownRecordTypeValue(value) => UnknownRecordTypeValue(value),
503 UnrecognizedLabelCode(value) => UnrecognizedLabelCode(value),
504 UnrecognizedNsec3Flags(flags) => UnrecognizedNsec3Flags(flags),
505 UnrecognizedCsyncFlags(flags) => UnrecognizedCsyncFlags(flags),
506
507 Io(ref e) => Io(if let Some(raw) = e.raw_os_error() {
509 io::Error::from_raw_os_error(raw)
510 } else {
511 io::Error::from(e.kind())
512 }),
513 Poisoned => Poisoned,
514 Ring(ref _e) => Ring(Unspecified),
515 SSL(ref e) => Msg(format!("there was an SSL error: {}", e)),
516 Timeout => Timeout,
517 Timer => Timer,
518 UrlParsing(ref e) => UrlParsing(*e),
519 Utf8(ref e) => Utf8(*e),
520 FromUtf8(ref e) => FromUtf8(e.clone()),
521 ParseInt(ref e) => ParseInt(e.clone()),
522 #[cfg(feature = "quinn")]
523 QuinnConnect(ref e) => QuinnConnect(e.clone()),
524 #[cfg(feature = "quinn")]
525 QuinnConnection(ref e) => QuinnConnection(e.clone()),
526 #[cfg(feature = "quinn")]
527 QuinnWriteError(ref e) => QuinnWriteError(e.clone()),
528 #[cfg(feature = "quinn")]
529 QuicMessageIdNot0(val) => QuicMessageIdNot0(val),
530 #[cfg(feature = "quinn")]
531 QuinnReadError(ref e) => QuinnReadError(e.clone()),
532 #[cfg(feature = "quinn")]
533 QuinnConfigError(ref e) => QuinnConfigError(e.clone()),
534 #[cfg(feature = "quinn")]
535 QuinnUnknownStreamError => QuinnUnknownStreamError,
536 #[cfg(feature = "rustls")]
537 RustlsError(ref e) => RustlsError(e.clone()),
538 }
539 }
540}
541
542pub trait FromProtoError: From<ProtoError> + std::error::Error + Clone {}
545
546impl<E> FromProtoError for E where E: From<ProtoError> + std::error::Error + Clone {}