hickory_proto/
error.rs

1// Copyright 2015-2020 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! Error types for the crate
9
10#![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/// Boolean for checking if backtrace is enabled at runtime
30#[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/// Generate a backtrace
39///
40/// If RUST_BACKTRACE is 1 or full then this will return Some(Backtrace), otherwise, NONE.
41#[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
56/// An alias for results returned by functions of this crate
57pub type ProtoResult<T> = ::std::result::Result<T, ProtoError>;
58
59/// The error kind for errors that get returned in the crate
60#[derive(Debug, EnumAsInner, Error)]
61#[non_exhaustive]
62pub enum ProtoErrorKind {
63    /// Query count is not one
64    #[error("there should only be one query per request, got: {0}")]
65    BadQueryCount(usize),
66
67    /// The underlying resource is too busy
68    ///
69    /// This is a signal that an internal resource is too busy. The intended action should be tried
70    /// again, ideally after waiting for a little while for the situation to improve. Alternatively,
71    /// the action could be tried on another resource (for example, in a name server pool).
72    #[error("resource too busy")]
73    Busy,
74
75    /// An error caused by a canceled future
76    #[error("future was canceled: {0:?}")]
77    Canceled(futures_channel::oneshot::Canceled),
78
79    /// Character data length exceeded the limit
80    #[error("char data length exceeds {max}: {len}")]
81    CharacterDataTooLong {
82        /// Specified maximum
83        max: usize,
84        /// Actual length
85        len: usize,
86    },
87
88    /// Overlapping labels
89    #[error("overlapping labels name {label} other {other}")]
90    LabelOverlapsWithOther {
91        /// Start of the label that is overlaps
92        label: usize,
93        /// Start of the other label
94        other: usize,
95    },
96
97    /// DNS protocol version doesn't have the expected version 3
98    #[error("dns key value unknown, must be 3: {0}")]
99    DnsKeyProtocolNot3(u8),
100
101    /// A domain name was too long
102    #[error("name label data exceed 255: {0}")]
103    DomainNameTooLong(usize),
104
105    /// EDNS resource record label is not the root label, although required
106    #[error("edns resource record label must be the root label (.): {0}")]
107    EdnsNameNotRoot(crate::rr::Name),
108
109    /// Format error in Message Parsing
110    #[error("message format error: {error}")]
111    FormError {
112        /// Header of the bad Message
113        header: Header,
114        /// Error that occured while parsing the Message
115        error: Box<ProtoError>,
116    },
117
118    /// An HMAC failed to verify
119    #[error("hmac validation failure")]
120    HmacInvalid(),
121
122    /// The length of rdata read was not as expected
123    #[error("incorrect rdata length read: {read} expected: {len}")]
124    IncorrectRDataLengthRead {
125        /// The amount of read data
126        read: usize,
127        /// The expected length of the data
128        len: usize,
129    },
130
131    /// Label bytes exceeded the limit of 63
132    #[error("label bytes exceed 63: {0}")]
133    LabelBytesTooLong(usize),
134
135    /// Label bytes exceeded the limit of 63
136    #[error("label points to data not prior to idx: {idx} ptr: {ptr}")]
137    PointerNotPriorToLabel {
138        /// index of the label containing this pointer
139        idx: usize,
140        /// location to which the pointer is directing
141        ptr: u16,
142    },
143
144    /// The maximum buffer size was exceeded
145    #[error("maximum buffer size exceeded: {0}")]
146    MaxBufferSizeExceeded(usize),
147
148    /// An error with an arbitrary message, referenced as &'static str
149    #[error("{0}")]
150    Message(&'static str),
151
152    /// An error with an arbitrary message, stored as String
153    #[error("{0}")]
154    Msg(String),
155
156    /// No error was specified
157    #[error("no error specified")]
158    NoError,
159
160    /// Not all records were able to be written
161    #[error("not all records could be written, wrote: {count}")]
162    NotAllRecordsWritten {
163        /// Number of records that were written before the error
164        count: usize,
165    },
166
167    /// Missing rrsigs
168    #[error("rrsigs are not present for record set name: {name} record_type: {record_type}")]
169    RrsigsNotPresent {
170        /// The record set name
171        name: Name,
172        /// The record type
173        record_type: RecordType,
174    },
175
176    /// An unknown algorithm type was found
177    #[error("algorithm type value unknown: {0}")]
178    UnknownAlgorithmTypeValue(u8),
179
180    /// An unknown dns class was found
181    #[error("dns class string unknown: {0}")]
182    UnknownDnsClassStr(String),
183
184    /// An unknown dns class value was found
185    #[error("dns class value unknown: {0}")]
186    UnknownDnsClassValue(u16),
187
188    /// An unknown record type string was found
189    #[error("record type string unknown: {0}")]
190    UnknownRecordTypeStr(String),
191
192    /// An unknown record type value was found
193    #[error("record type value unknown: {0}")]
194    UnknownRecordTypeValue(u16),
195
196    /// An unrecognized label code was found
197    #[error("unrecognized label code: {0:b}")]
198    UnrecognizedLabelCode(u8),
199
200    /// Unrecognized nsec3 flags were found
201    #[error("nsec3 flags should be 0b0000000*: {0:b}")]
202    UnrecognizedNsec3Flags(u8),
203
204    /// Unrecognized csync flags were found
205    #[error("csync flags should be 0b000000**: {0:b}")]
206    UnrecognizedCsyncFlags(u16),
207
208    // foreign
209    /// An error got returned from IO
210    #[error("io error: {0}")]
211    Io(io::Error),
212
213    /// Any sync poised error
214    #[error("lock poisoned error")]
215    Poisoned,
216
217    /// A ring error
218    #[error("ring error: {0}")]
219    Ring(#[from] Unspecified),
220
221    /// An ssl error
222    #[error("ssl error: {0}")]
223    SSL(#[from] SslErrorStack),
224
225    /// A tokio timer error
226    #[error("timer error")]
227    Timer,
228
229    /// A request timed out
230    #[error("request timed out")]
231    Timeout,
232
233    /// Tsig key verification failed
234    #[error("Tsig key wrong key error")]
235    TsigWrongKey,
236
237    /// Tsig unsupported mac algorithm
238    /// Supported algorithm documented in `TsigAlgorithm::supported` function.
239    #[cfg(feature = "dnssec")]
240    #[error("Tsig unsupported mac algorithm")]
241    TsigUnsupportedMacAlgorithm(TsigAlgorithm),
242
243    /// An url parsing error
244    #[error("url parsing error")]
245    UrlParsing(#[from] url::ParseError),
246
247    /// A utf8 parsing error
248    #[error("error parsing utf8 string")]
249    Utf8(#[from] std::str::Utf8Error),
250
251    /// A utf8 parsing error
252    #[error("error parsing utf8 string")]
253    FromUtf8(#[from] std::string::FromUtf8Error),
254
255    /// An int parsing error
256    #[error("error parsing int")]
257    ParseInt(#[from] std::num::ParseIntError),
258
259    /// A Quinn (Quic) connection error occurred
260    #[cfg(feature = "quinn")]
261    #[error("error creating quic connection: {0}")]
262    QuinnConnect(#[from] quinn::ConnectError),
263
264    /// A Quinn (QUIC) connection error occurred
265    #[cfg(feature = "quinn")]
266    #[error("error with quic connection: {0}")]
267    QuinnConnection(#[from] quinn::ConnectionError),
268
269    /// A Quinn (QUIC) write error occurred
270    #[cfg(feature = "quinn")]
271    #[error("error writing to quic connection: {0}")]
272    QuinnWriteError(#[from] quinn::WriteError),
273
274    /// A Quinn (QUIC) read error occurred
275    #[cfg(feature = "quinn")]
276    #[error("error writing to quic read: {0}")]
277    QuinnReadError(#[from] quinn::ReadExactError),
278
279    /// A Quinn (QUIC) configuration error occurred
280    #[cfg(feature = "quinn")]
281    #[error("error constructing quic configuration: {0}")]
282    QuinnConfigError(#[from] quinn::ConfigError),
283
284    /// Unknown QUIC stream used
285    #[cfg(feature = "quinn")]
286    #[error("an unknown quic stream was used")]
287    QuinnUnknownStreamError,
288
289    /// A quic message id should always be 0
290    #[cfg(feature = "quinn")]
291    #[error("quic messages should always be 0, got: {0}")]
292    QuicMessageIdNot0(u16),
293
294    /// A Rustls error occurred
295    #[cfg(feature = "rustls")]
296    #[error("rustls construction error: {0}")]
297    RustlsError(#[from] rustls::Error),
298
299    /// No valid certificates found in the native root store.
300    #[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/// The error type for errors that get returned in the crate
306#[derive(Error, Clone, Debug)]
307#[non_exhaustive]
308pub struct ProtoError {
309    /// Kind of error that ocurred
310    pub kind: Box<ProtoErrorKind>,
311    /// Backtrace to the source of the error
312    #[cfg(feature = "backtrace")]
313    pub backtrack: Option<ExtBacktrace>,
314}
315
316impl ProtoError {
317    /// Get the kind of the error
318    pub fn kind(&self) -> &ProtoErrorKind {
319        &self.kind
320    }
321
322    /// If this is a ProtoErrorKind::Busy
323    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            // foreign
474            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
513/// A trait marking a type which implements From<ProtoError> and
514/// std::error::Error types as well as Clone + Send
515pub 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
528/// An alias for dnssec results returned by functions of this crate
529pub type DnsSecResult<T> = ::std::result::Result<T, DnsSecError>;
530
531/// The error kind for dnssec errors that get returned in the crate
532#[allow(unreachable_pub)]
533#[derive(Debug, Error)]
534#[non_exhaustive]
535pub enum DnsSecErrorKind {
536    /// An error with an arbitrary message, referenced as &'static str
537    #[error("{0}")]
538    Message(&'static str),
539
540    /// An error with an arbitrary message, stored as String
541    #[error("{0}")]
542    Msg(String),
543
544    // foreign
545    /// An error got returned by the hickory-proto crate
546    #[error("proto error: {0}")]
547    Proto(#[from] ProtoError),
548
549    /// A ring error
550    #[error("ring error: {0}")]
551    RingKeyRejected(#[from] KeyRejected),
552
553    /// A ring error
554    #[error("ring error: {0}")]
555    RingUnspecified(#[from] Unspecified),
556
557    /// An ssl error
558    #[error("ssl error: {0}")]
559    SSL(#[from] SslErrorStack),
560
561    /// A request timed out
562    #[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            // foreign
574            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/// The error type for dnssec errors that get returned in the crate
584#[derive(Debug, Clone, Error)]
585pub struct DnsSecError {
586    kind: DnsSecErrorKind,
587    #[cfg(feature = "backtrace")]
588    backtrack: Option<ExtBacktrace>,
589}
590
591impl DnsSecError {
592    /// Get the kind of the error
593    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}