blob: 457343e236eaa07c391fc09d6778ebc825fa30b1 [file] [log] [blame]
// Copyright 2015-2020 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! Error types for the crate
#![deny(missing_docs)]
use std::{fmt, io, sync};
#[cfg(not(feature = "openssl"))]
use self::not_openssl::SslErrorStack;
#[cfg(not(feature = "ring"))]
use self::not_ring::Unspecified;
pub use backtrace::Backtrace as ExtBacktrace;
use lazy_static::lazy_static;
#[cfg(feature = "openssl")]
use openssl::error::ErrorStack as SslErrorStack;
#[cfg(feature = "ring")]
use ring::error::Unspecified;
use thiserror::Error;
use crate::rr::{Name, RecordType};
lazy_static! {
/// Boolean for checking if backtrace is enabled at runtime
pub static ref ENABLE_BACKTRACE: bool = {
use std::env;
let bt = env::var("RUST_BACKTRACE");
match bt.as_ref().map(|s| s as &str) {
Ok("full") | Ok("1") => true,
_ => false,
}
};
}
/// Generate a backtrace
///
/// If RUST_BACKTRACE is 1 or full then this will return Some(Backtrace), otherwise, NONE.
#[macro_export]
macro_rules! trace {
() => {{
use $crate::error::ExtBacktrace as Backtrace;
if *$crate::error::ENABLE_BACKTRACE {
Some(Backtrace::new())
} else {
None
}
}};
}
/// An alias for results returned by functions of this crate
pub type ProtoResult<T> = ::std::result::Result<T, ProtoError>;
/// The error kind for errors that get returned in the crate
#[derive(Debug, Error)]
pub enum ProtoErrorKind {
/// An error caused by a canceled future
#[error("future was canceled: {0:?}")]
Canceled(futures::channel::oneshot::Canceled),
/// Character data length exceeded the limit
#[error("char data length exceeds {max}: {len}")]
CharacterDataTooLong {
/// Specified maximum
max: usize,
/// Actual length
len: usize,
},
/// Overlapping labels
#[error("overlapping labels name {label} other {other}")]
LabelOverlapsWithOther {
/// Start of the label that is overlaps
label: usize,
/// Start of the other label
other: usize,
},
/// DNS protocol version doesn't have the expected version 3
#[error("dns key value unknown, must be 3: {0}")]
DnsKeyProtocolNot3(u8),
/// A domain name was too long
#[error("name label data exceed 255: {0}")]
DomainNameTooLong(usize),
/// EDNS resource record label is not the root label, although required
#[error("edns resource record label must be the root label (.): {0}")]
EdnsNameNotRoot(crate::rr::Name),
/// The length of rdata read was not as expected
#[error("incorrect rdata length read: {read} expected: {len}")]
IncorrectRDataLengthRead {
/// The amount of read data
read: usize,
/// The expected length of the data
len: usize,
},
/// Label bytes exceeded the limit of 63
#[error("label bytes exceed 63: {0}")]
LabelBytesTooLong(usize),
/// Label bytes exceeded the limit of 63
#[error("label points to data not prior to idx: {idx} ptr: {ptr}")]
PointerNotPriorToLabel {
/// index of the label containing this pointer
idx: usize,
/// location to which the pointer is directing
ptr: u16,
},
/// The maximum buffer size was exceeded
#[error("maximum buffer size exceeded: {0}")]
MaxBufferSizeExceeded(usize),
/// An error with an arbitrary message, referenced as &'static str
#[error("{0}")]
Message(&'static str),
/// An error with an arbitrary message, stored as String
#[error("{0}")]
Msg(String),
/// No error was specified
#[error("no error specified")]
NoError,
/// Not all records were able to be written
#[error("not all records could be written, wrote: {count}")]
NotAllRecordsWritten {
/// Number of records that were written before the error
count: usize,
},
/// Missing rrsigs
#[error("rrsigs are not present for record set name: {name} record_type: {record_type}")]
RrsigsNotPresent {
/// The record set name
name: Name,
/// The record type
record_type: RecordType,
},
/// An unknown algorithm type was found
#[error("algorithm type value unknown: {0}")]
UnknownAlgorithmTypeValue(u8),
/// An unknown dns class was found
#[error("dns class string unknown: {0}")]
UnknownDnsClassStr(String),
/// An unknown dns class value was found
#[error("dns class value unknown: {0}")]
UnknownDnsClassValue(u16),
/// An unknown record type string was found
#[error("record type string unknown: {0}")]
UnknownRecordTypeStr(String),
/// An unknown record type value was found
#[error("record type value unknown: {0}")]
UnknownRecordTypeValue(u16),
/// An unrecognized label code was found
#[error("unrecognized label code: {0:b}")]
UnrecognizedLabelCode(u8),
/// Unrecognized nsec3 flags were found
#[error("nsec3 flags should be 0b0000000*: {0:b}")]
UnrecognizedNsec3Flags(u8),
// foreign
/// An error got returned from IO
#[error("io error: {0}")]
Io(#[from] io::Error),
/// Any sync poised error
#[error("lock poisoned error")]
Poisoned,
/// A ring error
#[error("ring error: {0}")]
Ring(#[from] Unspecified),
/// An ssl error
#[error("ssl error: {0}")]
SSL(#[from] SslErrorStack),
/// A tokio timer error
#[error("timer error")]
Timer,
/// A request timed out
#[error("request timed out")]
Timeout,
/// An url parsing error
#[error("url parsing error")]
UrlParsing(#[from] url::ParseError),
/// A utf8 parsing error
#[error("error parsing utf8 string")]
Utf8(#[from] std::str::Utf8Error),
}
/// The error type for errors that get returned in the crate
#[derive(Error, Clone, Debug)]
pub struct ProtoError {
kind: ProtoErrorKind,
backtrack: Option<ExtBacktrace>,
}
impl ProtoError {
/// Get the kind of the error
pub fn kind(&self) -> &ProtoErrorKind {
&self.kind
}
}
impl fmt::Display for ProtoError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref backtrace) = self.backtrack {
fmt::Display::fmt(&self.kind, f)?;
fmt::Debug::fmt(backtrace, f)
} else {
fmt::Display::fmt(&self.kind, f)
}
}
}
impl From<ProtoErrorKind> for ProtoError {
fn from(kind: ProtoErrorKind) -> ProtoError {
ProtoError {
kind,
backtrack: trace!(),
}
}
}
impl From<&'static str> for ProtoError {
fn from(msg: &'static str) -> ProtoError {
ProtoErrorKind::Message(msg).into()
}
}
impl From<String> for ProtoError {
fn from(msg: String) -> ProtoError {
ProtoErrorKind::Msg(msg).into()
}
}
impl From<io::Error> for ProtoError {
fn from(e: io::Error) -> ProtoError {
match e.kind() {
io::ErrorKind::TimedOut => ProtoErrorKind::Timeout.into(),
_ => ProtoErrorKind::from(e).into(),
}
}
}
impl<T> From<sync::PoisonError<T>> for ProtoError {
fn from(_e: sync::PoisonError<T>) -> ProtoError {
ProtoErrorKind::Poisoned.into()
}
}
impl From<Unspecified> for ProtoError {
fn from(e: Unspecified) -> ProtoError {
ProtoErrorKind::from(e).into()
}
}
impl From<SslErrorStack> for ProtoError {
fn from(e: SslErrorStack) -> ProtoError {
ProtoErrorKind::from(e).into()
}
}
impl From<url::ParseError> for ProtoError {
fn from(e: url::ParseError) -> ProtoError {
ProtoErrorKind::from(e).into()
}
}
impl From<std::str::Utf8Error> for ProtoError {
fn from(e: std::str::Utf8Error) -> ProtoError {
ProtoErrorKind::from(e).into()
}
}
/// Stubs for running without OpenSSL
#[cfg(not(feature = "openssl"))]
pub mod not_openssl {
use std;
/// SslErrorStac stub
#[derive(Debug)]
pub struct SslErrorStack;
impl std::fmt::Display for SslErrorStack {
fn fmt(&self, _: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
Ok(())
}
}
impl std::error::Error for SslErrorStack {
fn description(&self) -> &str {
"openssl feature not enabled"
}
}
}
/// Types used without ring
#[cfg(not(feature = "ring"))]
pub mod not_ring {
use std;
/// The Unspecified error replacement
#[derive(Debug)]
pub struct Unspecified;
impl std::fmt::Display for Unspecified {
fn fmt(&self, _: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
Ok(())
}
}
impl std::error::Error for Unspecified {
fn description(&self) -> &str {
"ring feature not enabled"
}
}
}
impl From<ProtoError> for io::Error {
fn from(e: ProtoError) -> Self {
match *e.kind() {
ProtoErrorKind::Timeout => io::Error::new(io::ErrorKind::TimedOut, e),
_ => io::Error::new(io::ErrorKind::Other, e),
}
}
}
impl From<ProtoError> for String {
fn from(e: ProtoError) -> Self {
e.to_string()
}
}
#[cfg(feature = "wasm-bindgen")]
impl From<ProtoError> for wasm_bindgen_crate::JsValue {
fn from(e: ProtoError) -> Self {
js_sys::Error::new(&e.to_string()).into()
}
}
impl Clone for ProtoErrorKind {
fn clone(&self) -> Self {
use self::ProtoErrorKind::*;
match *self {
Canceled(ref c) => Canceled(*c),
CharacterDataTooLong { max, len } => CharacterDataTooLong { max, len },
LabelOverlapsWithOther { label, other } => LabelOverlapsWithOther { label, other },
DnsKeyProtocolNot3(protocol) => DnsKeyProtocolNot3(protocol),
DomainNameTooLong(len) => DomainNameTooLong(len),
EdnsNameNotRoot(ref found) => EdnsNameNotRoot(found.clone()),
IncorrectRDataLengthRead { read, len } => IncorrectRDataLengthRead { read, len },
LabelBytesTooLong(len) => LabelBytesTooLong(len),
PointerNotPriorToLabel { idx, ptr } => PointerNotPriorToLabel { idx, ptr },
MaxBufferSizeExceeded(max) => MaxBufferSizeExceeded(max),
Message(msg) => Message(msg),
Msg(ref msg) => Msg(msg.clone()),
NoError => NoError,
NotAllRecordsWritten { count } => NotAllRecordsWritten { count },
RrsigsNotPresent {
ref name,
ref record_type,
} => RrsigsNotPresent {
name: name.clone(),
record_type: *record_type,
},
UnknownAlgorithmTypeValue(value) => UnknownAlgorithmTypeValue(value),
UnknownDnsClassStr(ref value) => UnknownDnsClassStr(value.clone()),
UnknownDnsClassValue(value) => UnknownDnsClassValue(value),
UnknownRecordTypeStr(ref value) => UnknownRecordTypeStr(value.clone()),
UnknownRecordTypeValue(value) => UnknownRecordTypeValue(value),
UnrecognizedLabelCode(value) => UnrecognizedLabelCode(value),
UnrecognizedNsec3Flags(flags) => UnrecognizedNsec3Flags(flags),
// foreign
Io(ref e) => Io(io::Error::from(e.kind())),
Poisoned => Poisoned,
Ring(ref _e) => Ring(Unspecified),
SSL(ref e) => Msg(format!("there was an SSL error: {}", e)),
Timeout => Timeout,
Timer => Timer,
UrlParsing(ref e) => UrlParsing(*e),
Utf8(ref e) => Utf8(*e),
}
}
}
/// A trait marking a type which implements From<ProtoError> and
/// std::error::Error types as well as Clone + Send
pub trait FromProtoError: From<ProtoError> + std::error::Error + Clone {}
impl<E> FromProtoError for E where E: From<ProtoError> + std::error::Error + Clone {}