trust_dns_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// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://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(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    /// Boolean for checking if backtrace is enabled at runtime
38    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/// Generate a backtrace
46///
47/// If RUST_BACKTRACE is 1 or full then this will return Some(Backtrace), otherwise, NONE.
48#[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
63/// An alias for results returned by functions of this crate
64pub type ProtoResult<T> = ::std::result::Result<T, ProtoError>;
65
66/// The error kind for errors that get returned in the crate
67#[derive(Debug, EnumAsInner, Error)]
68#[non_exhaustive]
69pub enum ProtoErrorKind {
70    /// Query count is not one
71    #[error("there should only be one query per request, got: {0}")]
72    BadQueryCount(usize),
73
74    /// The underlying resource is too busy
75    ///
76    /// This is a signal that an internal resource is too busy. The intended action should be tried
77    /// again, ideally after waiting for a little while for the situation to improve. Alternatively,
78    /// the action could be tried on another resource (for example, in a name server pool).
79    #[error("resource too busy")]
80    Busy,
81
82    /// An error caused by a canceled future
83    #[error("future was canceled: {0:?}")]
84    Canceled(futures_channel::oneshot::Canceled),
85
86    /// Character data length exceeded the limit
87    #[error("char data length exceeds {max}: {len}")]
88    CharacterDataTooLong {
89        /// Specified maximum
90        max: usize,
91        /// Actual length
92        len: usize,
93    },
94
95    /// Overlapping labels
96    #[error("overlapping labels name {label} other {other}")]
97    LabelOverlapsWithOther {
98        /// Start of the label that is overlaps
99        label: usize,
100        /// Start of the other label
101        other: usize,
102    },
103
104    /// DNS protocol version doesn't have the expected version 3
105    #[error("dns key value unknown, must be 3: {0}")]
106    DnsKeyProtocolNot3(u8),
107
108    /// A domain name was too long
109    #[error("name label data exceed 255: {0}")]
110    DomainNameTooLong(usize),
111
112    /// EDNS resource record label is not the root label, although required
113    #[error("edns resource record label must be the root label (.): {0}")]
114    EdnsNameNotRoot(crate::rr::Name),
115
116    /// Format error in Message Parsing
117    #[error("message format error: {error}")]
118    FormError {
119        /// Header of the bad Message
120        header: Header,
121        /// Error that occured while parsing the Message
122        error: Box<ProtoError>,
123    },
124
125    /// An HMAC failed to verify
126    #[error("hmac validation failure")]
127    HmacInvalid(),
128
129    /// The length of rdata read was not as expected
130    #[error("incorrect rdata length read: {read} expected: {len}")]
131    IncorrectRDataLengthRead {
132        /// The amount of read data
133        read: usize,
134        /// The expected length of the data
135        len: usize,
136    },
137
138    /// Label bytes exceeded the limit of 63
139    #[error("label bytes exceed 63: {0}")]
140    LabelBytesTooLong(usize),
141
142    /// Label bytes exceeded the limit of 63
143    #[error("label points to data not prior to idx: {idx} ptr: {ptr}")]
144    PointerNotPriorToLabel {
145        /// index of the label containing this pointer
146        idx: usize,
147        /// location to which the pointer is directing
148        ptr: u16,
149    },
150
151    /// The maximum buffer size was exceeded
152    #[error("maximum buffer size exceeded: {0}")]
153    MaxBufferSizeExceeded(usize),
154
155    /// An error with an arbitrary message, referenced as &'static str
156    #[error("{0}")]
157    Message(&'static str),
158
159    /// An error with an arbitrary message, stored as String
160    #[error("{0}")]
161    Msg(String),
162
163    /// No error was specified
164    #[error("no error specified")]
165    NoError,
166
167    /// Not all records were able to be written
168    #[error("not all records could be written, wrote: {count}")]
169    NotAllRecordsWritten {
170        /// Number of records that were written before the error
171        count: usize,
172    },
173
174    /// Missing rrsigs
175    #[error("rrsigs are not present for record set name: {name} record_type: {record_type}")]
176    RrsigsNotPresent {
177        /// The record set name
178        name: Name,
179        /// The record type
180        record_type: RecordType,
181    },
182
183    /// An unknown algorithm type was found
184    #[error("algorithm type value unknown: {0}")]
185    UnknownAlgorithmTypeValue(u8),
186
187    /// An unknown dns class was found
188    #[error("dns class string unknown: {0}")]
189    UnknownDnsClassStr(String),
190
191    /// An unknown dns class value was found
192    #[error("dns class value unknown: {0}")]
193    UnknownDnsClassValue(u16),
194
195    /// An unknown record type string was found
196    #[error("record type string unknown: {0}")]
197    UnknownRecordTypeStr(String),
198
199    /// An unknown record type value was found
200    #[error("record type value unknown: {0}")]
201    UnknownRecordTypeValue(u16),
202
203    /// An unrecognized label code was found
204    #[error("unrecognized label code: {0:b}")]
205    UnrecognizedLabelCode(u8),
206
207    /// Unrecognized nsec3 flags were found
208    #[error("nsec3 flags should be 0b0000000*: {0:b}")]
209    UnrecognizedNsec3Flags(u8),
210
211    /// Unrecognized csync flags were found
212    #[error("csync flags should be 0b000000**: {0:b}")]
213    UnrecognizedCsyncFlags(u16),
214
215    // foreign
216    /// An error got returned from IO
217    #[error("io error: {0}")]
218    Io(io::Error),
219
220    /// Any sync poised error
221    #[error("lock poisoned error")]
222    Poisoned,
223
224    /// A ring error
225    #[error("ring error: {0}")]
226    Ring(#[from] Unspecified),
227
228    /// An ssl error
229    #[error("ssl error: {0}")]
230    SSL(#[from] SslErrorStack),
231
232    /// A tokio timer error
233    #[error("timer error")]
234    Timer,
235
236    /// A request timed out
237    #[error("request timed out")]
238    Timeout,
239
240    /// An url parsing error
241    #[error("url parsing error")]
242    UrlParsing(#[from] url::ParseError),
243
244    /// A utf8 parsing error
245    #[error("error parsing utf8 string")]
246    Utf8(#[from] std::str::Utf8Error),
247
248    /// A utf8 parsing error
249    #[error("error parsing utf8 string")]
250    FromUtf8(#[from] std::string::FromUtf8Error),
251
252    /// An int parsing error
253    #[error("error parsing int")]
254    ParseInt(#[from] std::num::ParseIntError),
255
256    /// A Quinn (Quic) connection error occured
257    #[cfg(feature = "quinn")]
258    #[error("error creating quic connection: {0}")]
259    QuinnConnect(#[from] quinn::ConnectError),
260
261    /// A Quinn (QUIC) connection error occured
262    #[cfg(feature = "quinn")]
263    #[error("error with quic connection: {0}")]
264    QuinnConnection(#[from] quinn::ConnectionError),
265
266    /// A Quinn (QUIC) write error occured
267    #[cfg(feature = "quinn")]
268    #[error("error writing to quic connection: {0}")]
269    QuinnWriteError(#[from] quinn::WriteError),
270
271    /// A Quinn (QUIC) read error occured
272    #[cfg(feature = "quinn")]
273    #[error("error writing to quic read: {0}")]
274    QuinnReadError(#[from] quinn::ReadExactError),
275
276    /// A Quinn (QUIC) configuration error occured
277    #[cfg(feature = "quinn")]
278    #[error("error constructing quic configuration: {0}")]
279    QuinnConfigError(#[from] quinn::ConfigError),
280
281    /// Unknown QUIC stream used
282    #[cfg(feature = "quinn")]
283    #[error("an unknown quic stream was used")]
284    QuinnUnknownStreamError,
285
286    /// A quic message id should always be 0
287    #[cfg(feature = "quinn")]
288    #[error("quic messages should always be 0, got: {0}")]
289    QuicMessageIdNot0(u16),
290
291    /// A Rustls error occured
292    #[cfg(feature = "rustls")]
293    #[error("rustls construction error: {0}")]
294    RustlsError(#[from] rustls::Error),
295}
296
297/// The error type for errors that get returned in the crate
298#[derive(Error, Clone, Debug)]
299#[non_exhaustive]
300pub struct ProtoError {
301    /// Kind of error that ocurred
302    pub kind: Box<ProtoErrorKind>,
303    /// Backtrace to the source of the error
304    #[cfg(feature = "backtrace")]
305    pub backtrack: Option<ExtBacktrace>,
306}
307
308impl ProtoError {
309    /// Get the kind of the error
310    pub fn kind(&self) -> &ProtoErrorKind {
311        &self.kind
312    }
313
314    /// If this is a ProtoErrorKind::Busy
315    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/// Stubs for running without OpenSSL
398#[cfg(not(feature = "openssl"))]
399#[cfg_attr(docsrs, doc(cfg(not(feature = "openssl"))))]
400pub mod not_openssl {
401    use std;
402
403    /// SslErrorStac stub
404    #[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/// Types used without ring
421#[cfg(not(feature = "ring"))]
422#[cfg_attr(docsrs, doc(cfg(not(feature = "ring"))))]
423pub mod not_ring {
424    use std;
425
426    /// The Unspecified error replacement
427    #[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            // foreign
508            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
542/// A trait marking a type which implements From<ProtoError> and
543/// std::error::Error types as well as Clone + Send
544pub trait FromProtoError: From<ProtoError> + std::error::Error + Clone {}
545
546impl<E> FromProtoError for E where E: From<ProtoError> + std::error::Error + Clone {}