1#![deny(missing_docs)]
11
12use std::{fmt, io, sync};
13
14#[cfg(feature = "backtrace")]
15#[cfg_attr(docsrs, doc(cfg(feature = "backtrace")))]
16pub use backtrace::Backtrace as ExtBacktrace;
17use enum_as_inner::EnumAsInner;
18#[cfg(feature = "backtrace")]
19use once_cell::sync::Lazy;
20use thiserror::Error;
21
22use crate::op::Header;
23
24#[cfg(feature = "dnssec")]
25use crate::rr::dnssec::rdata::tsig::TsigAlgorithm;
26use crate::rr::{Name, RecordType};
27use crate::serialize::binary::DecodeError;
28
29#[cfg(feature = "backtrace")]
31#[cfg_attr(docsrs, doc(cfg(feature = "backtrace")))]
32pub static ENABLE_BACKTRACE: Lazy<bool> = Lazy::new(|| {
33 use std::env;
34 let bt = env::var("RUST_BACKTRACE");
35 matches!(bt.as_ref().map(|s| s as &str), Ok("full") | Ok("1"))
36});
37
38#[cfg(feature = "backtrace")]
42#[cfg_attr(docsrs, doc(cfg(feature = "backtrace")))]
43#[macro_export]
44macro_rules! trace {
45 () => {{
46 use $crate::error::ExtBacktrace as Backtrace;
47
48 if *$crate::error::ENABLE_BACKTRACE {
49 Some(Backtrace::new())
50 } else {
51 None
52 }
53 }};
54}
55
56pub type ProtoResult<T> = ::std::result::Result<T, ProtoError>;
58
59#[derive(Debug, EnumAsInner, Error)]
61#[non_exhaustive]
62pub enum ProtoErrorKind {
63 #[error("there should only be one query per request, got: {0}")]
65 BadQueryCount(usize),
66
67 #[error("resource too busy")]
73 Busy,
74
75 #[error("future was canceled: {0:?}")]
77 Canceled(futures_channel::oneshot::Canceled),
78
79 #[error("char data length exceeds {max}: {len}")]
81 CharacterDataTooLong {
82 max: usize,
84 len: usize,
86 },
87
88 #[error("overlapping labels name {label} other {other}")]
90 LabelOverlapsWithOther {
91 label: usize,
93 other: usize,
95 },
96
97 #[error("dns key value unknown, must be 3: {0}")]
99 DnsKeyProtocolNot3(u8),
100
101 #[error("name label data exceed 255: {0}")]
103 DomainNameTooLong(usize),
104
105 #[error("edns resource record label must be the root label (.): {0}")]
107 EdnsNameNotRoot(crate::rr::Name),
108
109 #[error("message format error: {error}")]
111 FormError {
112 header: Header,
114 error: Box<ProtoError>,
116 },
117
118 #[error("hmac validation failure")]
120 HmacInvalid(),
121
122 #[error("incorrect rdata length read: {read} expected: {len}")]
124 IncorrectRDataLengthRead {
125 read: usize,
127 len: usize,
129 },
130
131 #[error("label bytes exceed 63: {0}")]
133 LabelBytesTooLong(usize),
134
135 #[error("label points to data not prior to idx: {idx} ptr: {ptr}")]
137 PointerNotPriorToLabel {
138 idx: usize,
140 ptr: u16,
142 },
143
144 #[error("maximum buffer size exceeded: {0}")]
146 MaxBufferSizeExceeded(usize),
147
148 #[error("{0}")]
150 Message(&'static str),
151
152 #[error("{0}")]
154 Msg(String),
155
156 #[error("no error specified")]
158 NoError,
159
160 #[error("not all records could be written, wrote: {count}")]
162 NotAllRecordsWritten {
163 count: usize,
165 },
166
167 #[error("rrsigs are not present for record set name: {name} record_type: {record_type}")]
169 RrsigsNotPresent {
170 name: Name,
172 record_type: RecordType,
174 },
175
176 #[error("algorithm type value unknown: {0}")]
178 UnknownAlgorithmTypeValue(u8),
179
180 #[error("dns class string unknown: {0}")]
182 UnknownDnsClassStr(String),
183
184 #[error("dns class value unknown: {0}")]
186 UnknownDnsClassValue(u16),
187
188 #[error("record type string unknown: {0}")]
190 UnknownRecordTypeStr(String),
191
192 #[error("record type value unknown: {0}")]
194 UnknownRecordTypeValue(u16),
195
196 #[error("unrecognized label code: {0:b}")]
198 UnrecognizedLabelCode(u8),
199
200 #[error("nsec3 flags should be 0b0000000*: {0:b}")]
202 UnrecognizedNsec3Flags(u8),
203
204 #[error("csync flags should be 0b000000**: {0:b}")]
206 UnrecognizedCsyncFlags(u16),
207
208 #[error("io error: {0}")]
211 Io(io::Error),
212
213 #[error("lock poisoned error")]
215 Poisoned,
216
217 #[error("ring error: {0}")]
219 Ring(#[from] Unspecified),
220
221 #[error("ssl error: {0}")]
223 SSL(#[from] SslErrorStack),
224
225 #[error("timer error")]
227 Timer,
228
229 #[error("request timed out")]
231 Timeout,
232
233 #[error("Tsig key wrong key error")]
235 TsigWrongKey,
236
237 #[cfg(feature = "dnssec")]
240 #[error("Tsig unsupported mac algorithm")]
241 TsigUnsupportedMacAlgorithm(TsigAlgorithm),
242
243 #[error("url parsing error")]
245 UrlParsing(#[from] url::ParseError),
246
247 #[error("error parsing utf8 string")]
249 Utf8(#[from] std::str::Utf8Error),
250
251 #[error("error parsing utf8 string")]
253 FromUtf8(#[from] std::string::FromUtf8Error),
254
255 #[error("error parsing int")]
257 ParseInt(#[from] std::num::ParseIntError),
258
259 #[cfg(feature = "quinn")]
261 #[error("error creating quic connection: {0}")]
262 QuinnConnect(#[from] quinn::ConnectError),
263
264 #[cfg(feature = "quinn")]
266 #[error("error with quic connection: {0}")]
267 QuinnConnection(#[from] quinn::ConnectionError),
268
269 #[cfg(feature = "quinn")]
271 #[error("error writing to quic connection: {0}")]
272 QuinnWriteError(#[from] quinn::WriteError),
273
274 #[cfg(feature = "quinn")]
276 #[error("error writing to quic read: {0}")]
277 QuinnReadError(#[from] quinn::ReadExactError),
278
279 #[cfg(feature = "quinn")]
281 #[error("error constructing quic configuration: {0}")]
282 QuinnConfigError(#[from] quinn::ConfigError),
283
284 #[cfg(feature = "quinn")]
286 #[error("an unknown quic stream was used")]
287 QuinnUnknownStreamError,
288
289 #[cfg(feature = "quinn")]
291 #[error("quic messages should always be 0, got: {0}")]
292 QuicMessageIdNot0(u16),
293
294 #[cfg(feature = "rustls")]
296 #[error("rustls construction error: {0}")]
297 RustlsError(#[from] rustls::Error),
298
299 #[cfg(all(feature = "native-certs", not(feature = "webpki-roots")))]
301 #[error("no valid certificates found in the native root store")]
302 NativeCerts,
303}
304
305#[derive(Error, Clone, Debug)]
307#[non_exhaustive]
308pub struct ProtoError {
309 pub kind: Box<ProtoErrorKind>,
311 #[cfg(feature = "backtrace")]
313 pub backtrack: Option<ExtBacktrace>,
314}
315
316impl ProtoError {
317 pub fn kind(&self) -> &ProtoErrorKind {
319 &self.kind
320 }
321
322 pub fn is_busy(&self) -> bool {
324 matches!(*self.kind, ProtoErrorKind::Busy)
325 }
326
327 pub(crate) fn as_dyn(&self) -> &(dyn std::error::Error + 'static) {
328 self
329 }
330}
331
332impl fmt::Display for ProtoError {
333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
334 cfg_if::cfg_if! {
335 if #[cfg(feature = "backtrace")] {
336 if let Some(ref backtrace) = self.backtrack {
337 fmt::Display::fmt(&self.kind, f)?;
338 fmt::Debug::fmt(backtrace, f)
339 } else {
340 fmt::Display::fmt(&self.kind, f)
341 }
342 } else {
343 fmt::Display::fmt(&self.kind, f)
344 }
345 }
346 }
347}
348
349impl<E> From<E> for ProtoError
350where
351 E: Into<ProtoErrorKind>,
352{
353 fn from(error: E) -> Self {
354 let kind: ProtoErrorKind = error.into();
355
356 Self {
357 kind: Box::new(kind),
358 #[cfg(feature = "backtrace")]
359 backtrack: trace!(),
360 }
361 }
362}
363
364impl From<DecodeError> for ProtoError {
365 fn from(err: DecodeError) -> Self {
366 match err {
367 DecodeError::PointerNotPriorToLabel { idx, ptr } => {
368 ProtoErrorKind::PointerNotPriorToLabel { idx, ptr }
369 }
370 DecodeError::LabelBytesTooLong(len) => ProtoErrorKind::LabelBytesTooLong(len),
371 DecodeError::UnrecognizedLabelCode(code) => ProtoErrorKind::UnrecognizedLabelCode(code),
372 DecodeError::DomainNameTooLong(len) => ProtoErrorKind::DomainNameTooLong(len),
373 DecodeError::LabelOverlapsWithOther { label, other } => {
374 ProtoErrorKind::LabelOverlapsWithOther { label, other }
375 }
376 _ => ProtoErrorKind::Msg(err.to_string()),
377 }
378 .into()
379 }
380}
381
382impl From<&'static str> for ProtoError {
383 fn from(msg: &'static str) -> Self {
384 ProtoErrorKind::Message(msg).into()
385 }
386}
387
388impl From<String> for ProtoError {
389 fn from(msg: String) -> Self {
390 ProtoErrorKind::Msg(msg).into()
391 }
392}
393
394impl From<io::Error> for ProtoErrorKind {
395 fn from(e: io::Error) -> Self {
396 match e.kind() {
397 io::ErrorKind::TimedOut => Self::Timeout,
398 _ => Self::Io(e),
399 }
400 }
401}
402
403impl<T> From<sync::PoisonError<T>> for ProtoError {
404 fn from(_e: sync::PoisonError<T>) -> Self {
405 ProtoErrorKind::Poisoned.into()
406 }
407}
408
409impl From<ProtoError> for io::Error {
410 fn from(e: ProtoError) -> Self {
411 match *e.kind() {
412 ProtoErrorKind::Timeout => Self::new(io::ErrorKind::TimedOut, e),
413 _ => Self::new(io::ErrorKind::Other, e),
414 }
415 }
416}
417
418impl From<ProtoError> for String {
419 fn from(e: ProtoError) -> Self {
420 e.to_string()
421 }
422}
423
424#[cfg(feature = "wasm-bindgen")]
425#[cfg_attr(docsrs, doc(cfg(feature = "wasm-bindgen")))]
426impl From<ProtoError> for wasm_bindgen_crate::JsValue {
427 fn from(e: ProtoError) -> Self {
428 js_sys::Error::new(&e.to_string()).into()
429 }
430}
431
432impl Clone for ProtoErrorKind {
433 fn clone(&self) -> Self {
434 use self::ProtoErrorKind::*;
435 match *self {
436 BadQueryCount(count) => BadQueryCount(count),
437 Busy => Busy,
438 Canceled(ref c) => Canceled(*c),
439 CharacterDataTooLong { max, len } => CharacterDataTooLong { max, len },
440 LabelOverlapsWithOther { label, other } => LabelOverlapsWithOther { label, other },
441 DnsKeyProtocolNot3(protocol) => DnsKeyProtocolNot3(protocol),
442 DomainNameTooLong(len) => DomainNameTooLong(len),
443 EdnsNameNotRoot(ref found) => EdnsNameNotRoot(found.clone()),
444 FormError { header, ref error } => FormError {
445 header,
446 error: error.clone(),
447 },
448 HmacInvalid() => HmacInvalid(),
449 IncorrectRDataLengthRead { read, len } => IncorrectRDataLengthRead { read, len },
450 LabelBytesTooLong(len) => LabelBytesTooLong(len),
451 PointerNotPriorToLabel { idx, ptr } => PointerNotPriorToLabel { idx, ptr },
452 MaxBufferSizeExceeded(max) => MaxBufferSizeExceeded(max),
453 Message(msg) => Message(msg),
454 Msg(ref msg) => Msg(msg.clone()),
455 NoError => NoError,
456 NotAllRecordsWritten { count } => NotAllRecordsWritten { count },
457 RrsigsNotPresent {
458 ref name,
459 ref record_type,
460 } => RrsigsNotPresent {
461 name: name.clone(),
462 record_type: *record_type,
463 },
464 UnknownAlgorithmTypeValue(value) => UnknownAlgorithmTypeValue(value),
465 UnknownDnsClassStr(ref value) => UnknownDnsClassStr(value.clone()),
466 UnknownDnsClassValue(value) => UnknownDnsClassValue(value),
467 UnknownRecordTypeStr(ref value) => UnknownRecordTypeStr(value.clone()),
468 UnknownRecordTypeValue(value) => UnknownRecordTypeValue(value),
469 UnrecognizedLabelCode(value) => UnrecognizedLabelCode(value),
470 UnrecognizedNsec3Flags(flags) => UnrecognizedNsec3Flags(flags),
471 UnrecognizedCsyncFlags(flags) => UnrecognizedCsyncFlags(flags),
472
473 Io(ref e) => Io(if let Some(raw) = e.raw_os_error() {
475 io::Error::from_raw_os_error(raw)
476 } else {
477 io::Error::from(e.kind())
478 }),
479 Poisoned => Poisoned,
480 Ring(ref _e) => Ring(Unspecified),
481 SSL(ref e) => Msg(format!("there was an SSL error: {e}")),
482 Timeout => Timeout,
483 Timer => Timer,
484 #[cfg(feature = "dnssec")]
485 TsigUnsupportedMacAlgorithm(ref alg) => TsigUnsupportedMacAlgorithm(alg.clone()),
486 TsigWrongKey => TsigWrongKey,
487 UrlParsing(ref e) => UrlParsing(*e),
488 Utf8(ref e) => Utf8(*e),
489 FromUtf8(ref e) => FromUtf8(e.clone()),
490 ParseInt(ref e) => ParseInt(e.clone()),
491 #[cfg(feature = "quinn")]
492 QuinnConnect(ref e) => QuinnConnect(e.clone()),
493 #[cfg(feature = "quinn")]
494 QuinnConnection(ref e) => QuinnConnection(e.clone()),
495 #[cfg(feature = "quinn")]
496 QuinnWriteError(ref e) => QuinnWriteError(e.clone()),
497 #[cfg(feature = "quinn")]
498 QuicMessageIdNot0(val) => QuicMessageIdNot0(val),
499 #[cfg(feature = "quinn")]
500 QuinnReadError(ref e) => QuinnReadError(e.clone()),
501 #[cfg(feature = "quinn")]
502 QuinnConfigError(ref e) => QuinnConfigError(e.clone()),
503 #[cfg(feature = "quinn")]
504 QuinnUnknownStreamError => QuinnUnknownStreamError,
505 #[cfg(feature = "rustls")]
506 RustlsError(ref e) => RustlsError(e.clone()),
507 #[cfg(all(feature = "native-certs", not(feature = "webpki-roots")))]
508 NativeCerts => NativeCerts,
509 }
510 }
511}
512
513pub trait FromProtoError: From<ProtoError> + std::error::Error + Clone {}
516
517impl<E> FromProtoError for E where E: From<ProtoError> + std::error::Error + Clone {}
518
519#[cfg(not(feature = "openssl"))]
520use self::not_openssl::SslErrorStack;
521#[cfg(not(feature = "ring"))]
522use self::not_ring::{KeyRejected, Unspecified};
523#[cfg(feature = "openssl")]
524use openssl::error::ErrorStack as SslErrorStack;
525#[cfg(feature = "ring")]
526use ring::error::{KeyRejected, Unspecified};
527
528pub type DnsSecResult<T> = ::std::result::Result<T, DnsSecError>;
530
531#[allow(unreachable_pub)]
533#[derive(Debug, Error)]
534#[non_exhaustive]
535pub enum DnsSecErrorKind {
536 #[error("{0}")]
538 Message(&'static str),
539
540 #[error("{0}")]
542 Msg(String),
543
544 #[error("proto error: {0}")]
547 Proto(#[from] ProtoError),
548
549 #[error("ring error: {0}")]
551 RingKeyRejected(#[from] KeyRejected),
552
553 #[error("ring error: {0}")]
555 RingUnspecified(#[from] Unspecified),
556
557 #[error("ssl error: {0}")]
559 SSL(#[from] SslErrorStack),
560
561 #[error("request timed out")]
563 Timeout,
564}
565
566impl Clone for DnsSecErrorKind {
567 fn clone(&self) -> Self {
568 use DnsSecErrorKind::*;
569 match self {
570 Message(msg) => Message(msg),
571 Msg(ref msg) => Msg(msg.clone()),
572
573 Proto(proto) => Proto(proto.clone()),
575 RingKeyRejected(r) => Msg(format!("Ring rejected key: {r}")),
576 RingUnspecified(_r) => RingUnspecified(Unspecified),
577 SSL(ssl) => Msg(format!("SSL had an error: {ssl}")),
578 Timeout => Timeout,
579 }
580 }
581}
582
583#[derive(Debug, Clone, Error)]
585pub struct DnsSecError {
586 kind: DnsSecErrorKind,
587 #[cfg(feature = "backtrace")]
588 backtrack: Option<ExtBacktrace>,
589}
590
591impl DnsSecError {
592 pub fn kind(&self) -> &DnsSecErrorKind {
594 &self.kind
595 }
596}
597
598impl fmt::Display for DnsSecError {
599 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
600 cfg_if::cfg_if! {
601 if #[cfg(feature = "backtrace")] {
602 if let Some(ref backtrace) = self.backtrack {
603 fmt::Display::fmt(&self.kind, f)?;
604 fmt::Debug::fmt(backtrace, f)
605 } else {
606 fmt::Display::fmt(&self.kind, f)
607 }
608 } else {
609 fmt::Display::fmt(&self.kind, f)
610 }
611 }
612 }
613}
614
615impl From<DnsSecErrorKind> for DnsSecError {
616 fn from(kind: DnsSecErrorKind) -> Self {
617 Self {
618 kind,
619 #[cfg(feature = "backtrace")]
620 backtrack: trace!(),
621 }
622 }
623}
624
625impl From<&'static str> for DnsSecError {
626 fn from(msg: &'static str) -> Self {
627 DnsSecErrorKind::Message(msg).into()
628 }
629}
630
631impl From<String> for DnsSecError {
632 fn from(msg: String) -> Self {
633 DnsSecErrorKind::Msg(msg).into()
634 }
635}
636
637impl From<ProtoError> for DnsSecError {
638 fn from(e: ProtoError) -> Self {
639 match *e.kind() {
640 ProtoErrorKind::Timeout => DnsSecErrorKind::Timeout.into(),
641 _ => DnsSecErrorKind::from(e).into(),
642 }
643 }
644}
645
646impl From<KeyRejected> for DnsSecError {
647 fn from(e: KeyRejected) -> Self {
648 DnsSecErrorKind::from(e).into()
649 }
650}
651
652impl From<Unspecified> for DnsSecError {
653 fn from(e: Unspecified) -> Self {
654 DnsSecErrorKind::from(e).into()
655 }
656}
657
658impl From<SslErrorStack> for DnsSecError {
659 fn from(e: SslErrorStack) -> Self {
660 DnsSecErrorKind::from(e).into()
661 }
662}
663
664#[doc(hidden)]
665#[allow(unreachable_pub)]
666#[cfg(not(feature = "openssl"))]
667#[cfg_attr(docsrs, doc(cfg(not(feature = "openssl"))))]
668pub mod not_openssl {
669 use std;
670
671 #[derive(Clone, Copy, Debug)]
672 pub struct SslErrorStack;
673
674 impl std::fmt::Display for SslErrorStack {
675 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
676 Ok(())
677 }
678 }
679
680 impl std::error::Error for SslErrorStack {
681 fn description(&self) -> &str {
682 "openssl feature not enabled"
683 }
684 }
685}
686
687#[doc(hidden)]
688#[allow(unreachable_pub)]
689#[cfg(not(feature = "ring"))]
690#[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
691pub mod not_ring {
692 use std;
693
694 #[derive(Clone, Copy, Debug)]
695 pub struct KeyRejected;
696
697 #[derive(Clone, Copy, Debug)]
698 pub struct Unspecified;
699
700 impl std::fmt::Display for KeyRejected {
701 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
702 Ok(())
703 }
704 }
705
706 impl std::error::Error for KeyRejected {
707 fn description(&self) -> &str {
708 "ring feature not enabled"
709 }
710 }
711
712 impl std::fmt::Display for Unspecified {
713 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
714 Ok(())
715 }
716 }
717
718 impl std::error::Error for Unspecified {
719 fn description(&self) -> &str {
720 "ring feature not enabled"
721 }
722 }
723}