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 alloc::borrow::ToOwned;
13use alloc::boxed::Box;
14use alloc::string::{String, ToString};
15use alloc::sync::Arc;
16use alloc::vec::Vec;
17use core::cmp::Ordering;
18use core::fmt;
19#[cfg(feature = "std")]
20use std::{io, sync};
21
22#[cfg(feature = "backtrace")]
23pub use backtrace::Backtrace as ExtBacktrace;
24use enum_as_inner::EnumAsInner;
25#[cfg(feature = "backtrace")]
26use once_cell::sync::Lazy;
27use thiserror::Error;
28use tracing::debug;
29
30#[cfg(feature = "__dnssec")]
31use crate::dnssec::Proof;
32#[cfg(any(feature = "dnssec-aws-lc-rs", feature = "dnssec-ring"))]
33use crate::dnssec::ring_like::Unspecified;
34use crate::op::{Header, Query, ResponseCode};
35use crate::rr::{Record, RecordType, domain::Name, rdata::SOA, resource::RecordRef};
36use crate::serialize::binary::DecodeError;
37use crate::xfer::DnsResponse;
38
39/// Boolean for checking if backtrace is enabled at runtime
40#[cfg(feature = "backtrace")]
41pub static ENABLE_BACKTRACE: Lazy<bool> = Lazy::new(|| {
42    use std::env;
43    let bt = env::var("RUST_BACKTRACE");
44    matches!(bt.as_ref().map(|s| s as &str), Ok("full") | Ok("1"))
45});
46
47/// Generate a backtrace
48///
49/// If RUST_BACKTRACE is 1 or full then this will return Some(Backtrace), otherwise, NONE.
50#[cfg(feature = "backtrace")]
51#[macro_export]
52macro_rules! trace {
53    () => {{
54        use $crate::ExtBacktrace as Backtrace;
55
56        if *$crate::ENABLE_BACKTRACE {
57            Some(Backtrace::new())
58        } else {
59            None
60        }
61    }};
62}
63
64/// An alias for results returned by functions of this crate
65pub(crate) type ProtoResult<T> = ::core::result::Result<T, ProtoError>;
66
67/// The error kind for errors that get returned in the crate
68#[derive(Debug, EnumAsInner, Error)]
69#[non_exhaustive]
70pub enum ProtoErrorKind {
71    /// Query count is not one
72    #[error("there should only be one query per request, got: {0}")]
73    BadQueryCount(usize),
74
75    /// The underlying resource is too busy
76    ///
77    /// This is a signal that an internal resource is too busy. The intended action should be tried
78    /// again, ideally after waiting for a little while for the situation to improve. Alternatively,
79    /// the action could be tried on another resource (for example, in a name server pool).
80    #[error("resource too busy")]
81    Busy,
82
83    /// An error caused by a canceled future
84    #[error("future was canceled: {0:?}")]
85    Canceled(futures_channel::oneshot::Canceled),
86
87    /// Character data length exceeded the limit
88    #[error("char data length exceeds {max}: {len}")]
89    CharacterDataTooLong {
90        /// Specified maximum
91        max: usize,
92        /// Actual length
93        len: usize,
94    },
95
96    /// Overlapping labels
97    #[error("overlapping labels name {label} other {other}")]
98    LabelOverlapsWithOther {
99        /// Start of the label that is overlaps
100        label: usize,
101        /// Start of the other label
102        other: usize,
103    },
104
105    /// No Records and there is a corresponding DNSSEC Proof for NSEC
106    #[cfg(feature = "__dnssec")]
107    #[error("DNSSEC Negative Record Response for {query}, {proof}")]
108    Nsec {
109        /// Query for which the NSEC was returned
110        query: Box<Query>,
111        /// DNSSEC proof of the record
112        proof: Proof,
113    },
114
115    /// DNS protocol version doesn't have the expected version 3
116    #[error("dns key value unknown, must be 3: {0}")]
117    DnsKeyProtocolNot3(u8),
118
119    /// A domain name was too long
120    #[error("name label data exceed 255: {0}")]
121    DomainNameTooLong(usize),
122
123    /// EDNS resource record label is not the root label, although required
124    #[error("edns resource record label must be the root label (.): {0}")]
125    EdnsNameNotRoot(crate::rr::Name),
126
127    /// Format error in Message Parsing
128    #[error("message format error: {error}")]
129    FormError {
130        /// Header of the bad Message
131        header: Header,
132        /// Error that occurred while parsing the Message
133        error: Box<ProtoError>,
134    },
135
136    /// The length of rdata read was not as expected
137    #[error("incorrect rdata length read: {read} expected: {len}")]
138    IncorrectRDataLengthRead {
139        /// The amount of read data
140        read: usize,
141        /// The expected length of the data
142        len: usize,
143    },
144
145    /// Label bytes exceeded the limit of 63
146    #[error("label bytes exceed 63: {0}")]
147    LabelBytesTooLong(usize),
148
149    /// Label bytes exceeded the limit of 63
150    #[error("label points to data not prior to idx: {idx} ptr: {ptr}")]
151    PointerNotPriorToLabel {
152        /// index of the label containing this pointer
153        idx: usize,
154        /// location to which the pointer is directing
155        ptr: u16,
156    },
157
158    /// The maximum buffer size was exceeded
159    #[error("maximum buffer size exceeded: {0}")]
160    MaxBufferSizeExceeded(usize),
161
162    /// Maximum record limit was exceeded
163    #[error("maximum record limit for {record_type} exceeded: {count} records")]
164    MaxRecordLimitExceeded {
165        /// number of records
166        count: usize,
167        /// The record type that triggered the error.
168        record_type: RecordType,
169    },
170
171    /// An error with an arbitrary message, referenced as &'static str
172    #[error("{0}")]
173    Message(&'static str),
174
175    /// An error with an arbitrary message, stored as String
176    #[error("{0}")]
177    Msg(String),
178
179    /// No resolvers available
180    #[error("no connections available")]
181    NoConnections,
182
183    /// No error was specified
184    #[error("no error specified")]
185    NoError,
186
187    /// Not all records were able to be written
188    #[error("not all records could be written, wrote: {count}")]
189    NotAllRecordsWritten {
190        /// Number of records that were written before the error
191        count: usize,
192    },
193
194    /// No records were found for a query
195    #[error("no records found for {:?}", query)]
196    NoRecordsFound {
197        /// The query for which no records were found.
198        query: Box<Query>,
199        /// If an SOA is present, then this is an authoritative response or a referral to another nameserver, see the negative_type field.
200        soa: Option<Box<Record<SOA>>>,
201        /// Nameservers may be present in addition to or in lieu of an SOA for a referral
202        /// The tuple struct layout is vec[(Nameserver, [vec of glue records])]
203        ns: Option<Arc<[ForwardNSData]>>,
204        /// negative ttl, as determined from DnsResponse::negative_ttl
205        ///  this will only be present if the SOA was also present.
206        negative_ttl: Option<u32>,
207        /// ResponseCode, if `NXDOMAIN`, the domain does not exist (and no other types).
208        ///   If `NoError`, then the domain exists but there exist either other types at the same label, or subzones of that label.
209        response_code: ResponseCode,
210        /// If we trust `NXDOMAIN` errors from this server
211        trusted: bool,
212        /// Authority records from the query. These are important to preserve for DNSSEC validation.
213        authorities: Option<Arc<[Record]>>,
214    },
215
216    /// An unknown algorithm type was found
217    #[error("algorithm type value unknown: {0}")]
218    UnknownAlgorithmTypeValue(u8),
219
220    /// An unknown digest type was found
221    #[error("digest type value unknown: {0}")]
222    UnknownDigestTypeValue(u8),
223
224    /// An unknown dns class was found
225    #[error("dns class string unknown: {0}")]
226    UnknownDnsClassStr(String),
227
228    /// An unknown dns class value was found
229    #[error("dns class value unknown: {0}")]
230    UnknownDnsClassValue(u16),
231
232    /// An unknown record type string was found
233    #[error("record type string unknown: {0}")]
234    UnknownRecordTypeStr(String),
235
236    /// An unknown record type value was found
237    #[error("record type value unknown: {0}")]
238    UnknownRecordTypeValue(u16),
239
240    /// An unrecognized label code was found
241    #[error("unrecognized label code: {0:b}")]
242    UnrecognizedLabelCode(u8),
243
244    /// Unrecognized nsec3 flags were found
245    #[error("nsec3 flags should be 0b0000000*: {0:b}")]
246    UnrecognizedNsec3Flags(u8),
247
248    /// Unrecognized csync flags were found
249    #[error("csync flags should be 0b000000**: {0:b}")]
250    UnrecognizedCsyncFlags(u16),
251
252    // foreign
253    /// An error got returned from IO
254    #[cfg(feature = "std")]
255    #[error("io error: {0}")]
256    Io(Arc<io::Error>),
257
258    /// Any sync poised error
259    #[error("lock poisoned error")]
260    Poisoned,
261
262    /// A request was Refused due to some access check
263    #[error("request refused")]
264    RequestRefused,
265
266    /// A ring error
267    #[cfg(feature = "__dnssec")]
268    #[error("ring error: {0}")]
269    Ring(#[from] Unspecified),
270
271    /// A tokio timer error
272    #[error("timer error")]
273    Timer,
274
275    /// A request timed out
276    #[error("request timed out")]
277    Timeout,
278
279    /// An url parsing error
280    #[error("url parsing error")]
281    UrlParsing(#[from] url::ParseError),
282
283    /// A utf8 parsing error
284    #[error("error parsing utf8 string")]
285    Utf8(#[from] core::str::Utf8Error),
286
287    /// A utf8 parsing error
288    #[error("error parsing utf8 string")]
289    FromUtf8(#[from] alloc::string::FromUtf8Error),
290
291    /// An int parsing error
292    #[error("error parsing int")]
293    ParseInt(#[from] core::num::ParseIntError),
294
295    /// A Quinn (Quic) connection error occurred
296    #[cfg(feature = "__quic")]
297    #[error("error creating quic connection: {0}")]
298    QuinnConnect(#[from] quinn::ConnectError),
299
300    /// A Quinn (QUIC) connection error occurred
301    #[cfg(feature = "__quic")]
302    #[error("error with quic connection: {0}")]
303    QuinnConnection(#[from] quinn::ConnectionError),
304
305    /// A Quinn (QUIC) write error occurred
306    #[cfg(feature = "__quic")]
307    #[error("error writing to quic connection: {0}")]
308    QuinnWriteError(#[from] quinn::WriteError),
309
310    /// A Quinn (QUIC) read error occurred
311    #[cfg(feature = "__quic")]
312    #[error("error writing to quic read: {0}")]
313    QuinnReadError(#[from] quinn::ReadExactError),
314
315    /// A Quinn (QUIC) read error occurred
316    #[cfg(feature = "__quic")]
317    #[error("referenced a closed QUIC stream: {0}")]
318    QuinnStreamError(#[from] quinn::ClosedStream),
319
320    /// A Quinn (QUIC) configuration error occurred
321    #[cfg(feature = "__quic")]
322    #[error("error constructing quic configuration: {0}")]
323    QuinnConfigError(#[from] quinn::ConfigError),
324
325    /// QUIC TLS config must include an AES-128-GCM cipher suite
326    #[cfg(feature = "__quic")]
327    #[error("QUIC TLS config must include an AES-128-GCM cipher suite")]
328    QuinnTlsConfigError(#[from] quinn::crypto::rustls::NoInitialCipherSuite),
329
330    /// Unknown QUIC stream used
331    #[cfg(feature = "__quic")]
332    #[error("an unknown quic stream was used")]
333    QuinnUnknownStreamError,
334
335    /// A quic message id should always be 0
336    #[cfg(feature = "__quic")]
337    #[error("quic messages should always be 0, got: {0}")]
338    QuicMessageIdNot0(u16),
339
340    /// A Rustls error occurred
341    #[cfg(feature = "__tls")]
342    #[error("rustls construction error: {0}")]
343    RustlsError(#[from] rustls::Error),
344
345    /// Case randomization is enabled, and a server did not echo a query name back with the same
346    /// case.
347    #[error("case of query name in response did not match")]
348    QueryCaseMismatch,
349}
350
351/// Data needed to process a SOA-record-based referral.
352#[derive(Clone, Debug)]
353pub struct ForwardData {
354    /// Query
355    pub query: Box<Query>,
356    /// Name
357    pub name: Name,
358    /// SOA
359    pub soa: Box<Record<SOA>>,
360    /// No records found?
361    no_records_found: bool,
362    /// IS nx domain?
363    nx_domain: bool,
364    /// Authority records
365    pub authorities: Option<Arc<[Record]>>,
366}
367
368impl ForwardData {
369    /// Construct a new ForwardData
370    pub fn new(
371        query: Box<Query>,
372        name: Name,
373        soa: Box<Record<SOA>>,
374        no_records_found: bool,
375        nx_domain: bool,
376        authorities: Option<Arc<[Record]>>,
377    ) -> Self {
378        Self {
379            query,
380            name,
381            soa,
382            no_records_found,
383            nx_domain,
384            authorities,
385        }
386    }
387
388    /// are there records?
389    pub fn is_no_records_found(&self) -> bool {
390        self.no_records_found
391    }
392
393    /// is this nxdomain?
394    pub fn is_nx_domain(&self) -> bool {
395        self.nx_domain
396    }
397}
398
399/// Data needed to process a NS-record-based referral.
400#[derive(Clone, Debug)]
401pub struct ForwardNSData {
402    /// The referant NS record
403    pub ns: Record,
404    /// Any glue records associated with the referant NS record.
405    pub glue: Arc<[Record]>,
406}
407
408/// The error type for errors that get returned in the crate
409#[derive(Error, Clone, Debug)]
410#[non_exhaustive]
411pub struct ProtoError {
412    /// Kind of error that occurred
413    pub kind: Box<ProtoErrorKind>,
414    /// Backtrace to the source of the error
415    #[cfg(feature = "backtrace")]
416    pub backtrack: Option<ExtBacktrace>,
417}
418
419impl ProtoError {
420    /// Constructor to NX type errors
421    #[inline]
422    pub fn nx_error(
423        query: Box<Query>,
424        soa: Option<Box<Record<SOA>>>,
425        ns: Option<Arc<[ForwardNSData]>>,
426        negative_ttl: Option<u32>,
427        response_code: ResponseCode,
428        trusted: bool,
429        authorities: Option<Arc<[Record]>>,
430    ) -> Self {
431        ProtoErrorKind::NoRecordsFound {
432            query,
433            soa,
434            ns,
435            negative_ttl,
436            response_code,
437            trusted,
438            authorities,
439        }
440        .into()
441    }
442
443    /// Get the kind of the error
444    #[inline]
445    pub fn kind(&self) -> &ProtoErrorKind {
446        &self.kind
447    }
448
449    /// If this is a ProtoErrorKind::Busy
450    #[inline]
451    pub fn is_busy(&self) -> bool {
452        matches!(*self.kind, ProtoErrorKind::Busy)
453    }
454
455    /// Returns true if this error represents NoConnections
456    #[inline]
457    pub fn is_no_connections(&self) -> bool {
458        matches!(*self.kind, ProtoErrorKind::NoConnections)
459    }
460
461    /// Returns true if the domain does not exist
462    #[inline]
463    pub fn is_nx_domain(&self) -> bool {
464        matches!(
465            *self.kind,
466            ProtoErrorKind::NoRecordsFound {
467                response_code: ResponseCode::NXDomain,
468                ..
469            }
470        )
471    }
472
473    /// Returns true if the error represents NoRecordsFound
474    #[inline]
475    pub fn is_no_records_found(&self) -> bool {
476        matches!(*self.kind, ProtoErrorKind::NoRecordsFound { .. })
477    }
478
479    /// Returns the SOA record, if the error contains one
480    #[inline]
481    pub fn into_soa(self) -> Option<Box<Record<SOA>>> {
482        match *self.kind {
483            ProtoErrorKind::NoRecordsFound { soa, .. } => soa,
484            _ => None,
485        }
486    }
487
488    /// Returns true if this is a std::io::Error
489    #[inline]
490    #[cfg(feature = "std")]
491    pub fn is_io(&self) -> bool {
492        matches!(*self.kind, ProtoErrorKind::Io(..))
493    }
494
495    #[cfg(feature = "std")]
496    pub(crate) fn as_dyn(&self) -> &(dyn std::error::Error + 'static) {
497        self
498    }
499
500    /// A conversion to determine if the response is an error
501    pub fn from_response(response: DnsResponse, trust_nx: bool) -> Result<DnsResponse, Self> {
502        use ResponseCode::*;
503        debug!("response: {}", *response);
504
505        match response.response_code() {
506                code @ ServFail
507                | code @ Refused
508                | code @ FormErr
509                | code @ NotImp
510                | code @ YXDomain
511                | code @ YXRRSet
512                | code @ NXRRSet
513                | code @ NotAuth
514                | code @ NotZone
515                | code @ BADVERS
516                | code @ BADSIG
517                | code @ BADKEY
518                | code @ BADTIME
519                | code @ BADMODE
520                | code @ BADNAME
521                | code @ BADALG
522                | code @ BADTRUNC
523                | code @ BADCOOKIE => {
524                    let soa = response.soa().as_ref().map(RecordRef::to_owned);
525                    let query = response.queries().iter().next().cloned().unwrap_or_default();
526                    let error_kind = ProtoErrorKind::NoRecordsFound {
527                        query: Box::new(query),
528                        ns: None,
529                        soa: soa.map(Box::new),
530                        negative_ttl: None,
531                        response_code: code,
532                        // This is marked as false as these are all potentially temporary error Response codes about
533                        //   the client and server interaction, and do not pertain to record existence.
534                        trusted: false,
535                        authorities: None,
536                    };
537
538                    Err(Self::from(error_kind))
539                }
540                // Some NXDOMAIN responses contain CNAME referrals, that will not be an error
541                code @ NXDomain |
542                // No answers are available, CNAME referrals are not failures
543                code @ NoError
544                if !response.contains_answer() && !response.truncated() => {
545                    // TODO: if authoritative, this is cacheable, store a TTL (currently that requires time, need a "now" here)
546                    // let valid_until = if response.authoritative() { now + response.negative_ttl() };
547                    let soa = response.soa().as_ref().map(RecordRef::to_owned);
548
549                    // Collect any referral nameservers and associated glue records
550                    let mut referral_name_servers = vec![];
551                    for ns in response.name_servers().iter().filter(|ns| ns.record_type() == RecordType::NS) {
552                        let glue = response
553                            .additionals()
554                            .iter()
555                            .filter_map(|record| {
556                                if let Some(ns_data) = ns.data().as_ns() {
557                                    if *record.name() == **ns_data &&
558                                       (record.data().as_a().is_some() || record.data().as_aaaa().is_some()) {
559                                           return Some(Record::to_owned(record));
560                                       }
561                                }
562
563                                None
564                            })
565                            .collect::<Vec<Record>>();
566                        referral_name_servers.push(ForwardNSData { ns: Record::to_owned(ns), glue: glue.into() })
567                    }
568
569                    let option_ns = if !referral_name_servers.is_empty() {
570                        Some(referral_name_servers.into())
571                    } else {
572                        None
573                    };
574
575                    let authorities = if ! response.name_servers().is_empty() {
576                        Some(response.name_servers().to_owned().into())
577                    } else {
578                        None
579                    };
580
581                    let negative_ttl = response.negative_ttl();
582                    // Note: improperly configured servers may do recursive lookups and return bad SOA
583                    // records here via AS112 (blackhole-1.iana.org. etc)
584                    // Such servers should be marked not trusted, as they may break reverse lookups
585                    // for local hosts.
586                    let trusted = trust_nx && soa.is_some();
587                    let query = response.into_message().take_queries().drain(..).next().unwrap_or_default();
588
589                    let error_kind = ProtoErrorKind::NoRecordsFound {
590                        query: Box::new(query),
591                        soa: soa.map(Box::new),
592                        ns: option_ns,
593                        negative_ttl,
594                        response_code: code,
595                        trusted,
596                        authorities,
597                    };
598
599                    Err(Self::from(error_kind))
600                }
601                NXDomain
602                | NoError
603                | Unknown(_) => Ok(response),
604            }
605    }
606
607    /// Compare two errors to see if one contains a server response.
608    pub fn cmp_specificity(&self, other: &Self) -> Ordering {
609        let kind = self.kind();
610        let other = other.kind();
611
612        match (kind, other) {
613            (ProtoErrorKind::NoRecordsFound { .. }, ProtoErrorKind::NoRecordsFound { .. }) => {
614                return Ordering::Equal;
615            }
616            (ProtoErrorKind::NoRecordsFound { .. }, _) => return Ordering::Greater,
617            (_, ProtoErrorKind::NoRecordsFound { .. }) => return Ordering::Less,
618            _ => (),
619        }
620
621        match (kind, other) {
622            #[cfg(feature = "std")]
623            (ProtoErrorKind::Io { .. }, ProtoErrorKind::Io { .. }) => return Ordering::Equal,
624            #[cfg(feature = "std")]
625            (ProtoErrorKind::Io { .. }, _) => return Ordering::Greater,
626            #[cfg(feature = "std")]
627            (_, ProtoErrorKind::Io { .. }) => return Ordering::Less,
628            _ => (),
629        }
630
631        match (kind, other) {
632            (ProtoErrorKind::Timeout, ProtoErrorKind::Timeout) => return Ordering::Equal,
633            (ProtoErrorKind::Timeout, _) => return Ordering::Greater,
634            (_, ProtoErrorKind::Timeout) => return Ordering::Less,
635            _ => (),
636        }
637
638        Ordering::Equal
639    }
640}
641
642impl fmt::Display for ProtoError {
643    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
644        cfg_if::cfg_if! {
645            if #[cfg(feature = "backtrace")] {
646                if let Some(backtrace) = &self.backtrack {
647                    fmt::Display::fmt(&self.kind, f)?;
648                    fmt::Debug::fmt(backtrace, f)
649                } else {
650                    fmt::Display::fmt(&self.kind, f)
651                }
652            } else {
653                fmt::Display::fmt(&self.kind, f)
654            }
655        }
656    }
657}
658
659impl<E> From<E> for ProtoError
660where
661    E: Into<ProtoErrorKind>,
662{
663    fn from(error: E) -> Self {
664        let kind: ProtoErrorKind = error.into();
665
666        Self {
667            kind: Box::new(kind),
668            #[cfg(feature = "backtrace")]
669            backtrack: trace!(),
670        }
671    }
672}
673
674impl From<DecodeError> for ProtoError {
675    fn from(err: DecodeError) -> Self {
676        match err {
677            DecodeError::PointerNotPriorToLabel { idx, ptr } => {
678                ProtoErrorKind::PointerNotPriorToLabel { idx, ptr }
679            }
680            DecodeError::LabelBytesTooLong(len) => ProtoErrorKind::LabelBytesTooLong(len),
681            DecodeError::UnrecognizedLabelCode(code) => ProtoErrorKind::UnrecognizedLabelCode(code),
682            DecodeError::DomainNameTooLong(len) => ProtoErrorKind::DomainNameTooLong(len),
683            DecodeError::LabelOverlapsWithOther { label, other } => {
684                ProtoErrorKind::LabelOverlapsWithOther { label, other }
685            }
686            _ => ProtoErrorKind::Msg(err.to_string()),
687        }
688        .into()
689    }
690}
691
692impl From<&'static str> for ProtoError {
693    fn from(msg: &'static str) -> Self {
694        ProtoErrorKind::Message(msg).into()
695    }
696}
697
698impl From<String> for ProtoError {
699    fn from(msg: String) -> Self {
700        ProtoErrorKind::Msg(msg).into()
701    }
702}
703
704#[cfg(feature = "std")]
705impl From<io::Error> for ProtoErrorKind {
706    fn from(e: io::Error) -> Self {
707        match e.kind() {
708            io::ErrorKind::TimedOut => Self::Timeout,
709            _ => Self::Io(e.into()),
710        }
711    }
712}
713
714#[cfg(feature = "std")]
715impl<T> From<sync::PoisonError<T>> for ProtoError {
716    fn from(_e: sync::PoisonError<T>) -> Self {
717        ProtoErrorKind::Poisoned.into()
718    }
719}
720
721#[cfg(feature = "std")]
722impl From<ProtoError> for io::Error {
723    fn from(e: ProtoError) -> Self {
724        match e.kind() {
725            ProtoErrorKind::Timeout => Self::new(io::ErrorKind::TimedOut, e),
726            _ => Self::new(io::ErrorKind::Other, e),
727        }
728    }
729}
730
731impl From<ProtoError> for String {
732    fn from(e: ProtoError) -> Self {
733        e.to_string()
734    }
735}
736
737#[cfg(feature = "wasm-bindgen")]
738impl From<ProtoError> for wasm_bindgen_crate::JsValue {
739    fn from(e: ProtoError) -> Self {
740        js_sys::Error::new(&e.to_string()).into()
741    }
742}
743
744impl Clone for ProtoErrorKind {
745    fn clone(&self) -> Self {
746        use self::ProtoErrorKind::*;
747        match *self {
748            BadQueryCount(count) => BadQueryCount(count),
749            Busy => Busy,
750            Canceled(ref c) => Canceled(*c),
751            CharacterDataTooLong { max, len } => CharacterDataTooLong { max, len },
752            LabelOverlapsWithOther { label, other } => LabelOverlapsWithOther { label, other },
753            DnsKeyProtocolNot3(protocol) => DnsKeyProtocolNot3(protocol),
754            DomainNameTooLong(len) => DomainNameTooLong(len),
755            EdnsNameNotRoot(ref found) => EdnsNameNotRoot(found.clone()),
756            FormError { header, ref error } => FormError {
757                header,
758                error: error.clone(),
759            },
760            IncorrectRDataLengthRead { read, len } => IncorrectRDataLengthRead { read, len },
761            LabelBytesTooLong(len) => LabelBytesTooLong(len),
762            PointerNotPriorToLabel { idx, ptr } => PointerNotPriorToLabel { idx, ptr },
763            MaxBufferSizeExceeded(max) => MaxBufferSizeExceeded(max),
764            MaxRecordLimitExceeded { count, record_type } => {
765                MaxRecordLimitExceeded { count, record_type }
766            }
767            Message(msg) => Message(msg),
768            Msg(ref msg) => Msg(msg.clone()),
769            NoConnections => NoConnections,
770            NoError => NoError,
771            NotAllRecordsWritten { count } => NotAllRecordsWritten { count },
772            NoRecordsFound {
773                ref query,
774                ref soa,
775                ref ns,
776                negative_ttl,
777                response_code,
778                trusted,
779                ref authorities,
780            } => NoRecordsFound {
781                query: query.clone(),
782                soa: soa.clone(),
783                ns: ns.clone(),
784                negative_ttl,
785                response_code,
786                trusted,
787                authorities: authorities.clone(),
788            },
789            RequestRefused => RequestRefused,
790            #[cfg(feature = "__dnssec")]
791            Nsec { ref query, proof } => Nsec {
792                query: query.clone(),
793                proof,
794            },
795            UnknownAlgorithmTypeValue(value) => UnknownAlgorithmTypeValue(value),
796            UnknownDigestTypeValue(value) => UnknownDigestTypeValue(value),
797            UnknownDnsClassStr(ref value) => UnknownDnsClassStr(value.clone()),
798            UnknownDnsClassValue(value) => UnknownDnsClassValue(value),
799            UnknownRecordTypeStr(ref value) => UnknownRecordTypeStr(value.clone()),
800            UnknownRecordTypeValue(value) => UnknownRecordTypeValue(value),
801            UnrecognizedLabelCode(value) => UnrecognizedLabelCode(value),
802            UnrecognizedNsec3Flags(flags) => UnrecognizedNsec3Flags(flags),
803            UnrecognizedCsyncFlags(flags) => UnrecognizedCsyncFlags(flags),
804            #[cfg(feature = "std")]
805            Io(ref e) => Io(e.clone()),
806            Poisoned => Poisoned,
807            #[cfg(feature = "__dnssec")]
808            Ring(ref _e) => Ring(Unspecified),
809            Timeout => Timeout,
810            Timer => Timer,
811            UrlParsing(ref e) => UrlParsing(*e),
812            Utf8(ref e) => Utf8(*e),
813            FromUtf8(ref e) => FromUtf8(e.clone()),
814            ParseInt(ref e) => ParseInt(e.clone()),
815            #[cfg(feature = "__quic")]
816            QuinnConnect(ref e) => QuinnConnect(e.clone()),
817            #[cfg(feature = "__quic")]
818            QuinnConnection(ref e) => QuinnConnection(e.clone()),
819            #[cfg(feature = "__quic")]
820            QuinnWriteError(ref e) => QuinnWriteError(e.clone()),
821            #[cfg(feature = "__quic")]
822            QuicMessageIdNot0(val) => QuicMessageIdNot0(val),
823            #[cfg(feature = "__quic")]
824            QuinnReadError(ref e) => QuinnReadError(e.clone()),
825            #[cfg(feature = "__quic")]
826            QuinnStreamError(ref e) => QuinnStreamError(e.clone()),
827            #[cfg(feature = "__quic")]
828            QuinnConfigError(ref e) => QuinnConfigError(e.clone()),
829            #[cfg(feature = "__quic")]
830            QuinnTlsConfigError(ref e) => QuinnTlsConfigError(e.clone()),
831            #[cfg(feature = "__quic")]
832            QuinnUnknownStreamError => QuinnUnknownStreamError,
833            #[cfg(feature = "__tls")]
834            RustlsError(ref e) => RustlsError(e.clone()),
835            QueryCaseMismatch => QueryCaseMismatch,
836        }
837    }
838}