#![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;
#[cfg(feature = "backtrace")]
pub use backtrace::Backtrace as ExtBacktrace;
#[cfg(feature = "backtrace")]
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};
#[cfg(feature = "backtrace")]
lazy_static! {
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,
}
};
}
#[cfg(feature = "backtrace")]
#[macro_export]
macro_rules! trace {
() => {{
use $crate::error::ExtBacktrace as Backtrace;
if *$crate::error::ENABLE_BACKTRACE {
Some(Backtrace::new())
} else {
None
}
}};
}
pub type ProtoResult<T> = ::std::result::Result<T, ProtoError>;
#[derive(Debug, Error)]
pub enum ProtoErrorKind {
#[error("future was canceled: {0:?}")]
Canceled(futures::channel::oneshot::Canceled),
#[error("char data length exceeds {max}: {len}")]
CharacterDataTooLong {
max: usize,
len: usize,
},
#[error("overlapping labels name {label} other {other}")]
LabelOverlapsWithOther {
label: usize,
other: usize,
},
#[error("dns key value unknown, must be 3: {0}")]
DnsKeyProtocolNot3(u8),
#[error("name label data exceed 255: {0}")]
DomainNameTooLong(usize),
#[error("edns resource record label must be the root label (.): {0}")]
EdnsNameNotRoot(crate::rr::Name),
#[error("incorrect rdata length read: {read} expected: {len}")]
IncorrectRDataLengthRead {
read: usize,
len: usize,
},
#[error("label bytes exceed 63: {0}")]
LabelBytesTooLong(usize),
#[error("label points to data not prior to idx: {idx} ptr: {ptr}")]
PointerNotPriorToLabel {
idx: usize,
ptr: u16,
},
#[error("maximum buffer size exceeded: {0}")]
MaxBufferSizeExceeded(usize),
#[error("{0}")]
Message(&'static str),
#[error("{0}")]
Msg(String),
#[error("no error specified")]
NoError,
#[error("not all records could be written, wrote: {count}")]
NotAllRecordsWritten {
count: usize,
},
#[error("rrsigs are not present for record set name: {name} record_type: {record_type}")]
RrsigsNotPresent {
name: Name,
record_type: RecordType,
},
#[error("algorithm type value unknown: {0}")]
UnknownAlgorithmTypeValue(u8),
#[error("dns class string unknown: {0}")]
UnknownDnsClassStr(String),
#[error("dns class value unknown: {0}")]
UnknownDnsClassValue(u16),
#[error("record type string unknown: {0}")]
UnknownRecordTypeStr(String),
#[error("record type value unknown: {0}")]
UnknownRecordTypeValue(u16),
#[error("unrecognized label code: {0:b}")]
UnrecognizedLabelCode(u8),
#[error("nsec3 flags should be 0b0000000*: {0:b}")]
UnrecognizedNsec3Flags(u8),
#[error("io error: {0}")]
Io(#[from] io::Error),
#[error("lock poisoned error")]
Poisoned,
#[error("ring error: {0}")]
Ring(#[from] Unspecified),
#[error("ssl error: {0}")]
SSL(#[from] SslErrorStack),
#[error("timer error")]
Timer,
#[error("request timed out")]
Timeout,
#[error("url parsing error")]
UrlParsing(#[from] url::ParseError),
#[error("error parsing utf8 string")]
Utf8(#[from] std::str::Utf8Error),
}
#[derive(Error, Clone, Debug)]
pub struct ProtoError {
kind: ProtoErrorKind,
#[cfg(feature = "backtrace")]
backtrack: Option<ExtBacktrace>,
}
impl ProtoError {
pub fn kind(&self) -> &ProtoErrorKind {
&self.kind
}
}
impl fmt::Display for ProtoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
cfg_if::cfg_if! {
if #[cfg(feature = "backtrace")] {
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)
}
} else {
fmt::Display::fmt(&self.kind, f)
}
}
}
}
impl From<ProtoErrorKind> for ProtoError {
fn from(kind: ProtoErrorKind) -> ProtoError {
ProtoError {
kind,
#[cfg(feature = "backtrace")]
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()
}
}
#[cfg(not(feature = "openssl"))]
pub mod not_openssl {
use std;
#[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"
}
}
}
#[cfg(not(feature = "ring"))]
pub mod not_ring {
use std;
#[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),
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),
}
}
}
pub trait FromProtoError: From<ProtoError> + std::error::Error + Clone {}
impl<E> FromProtoError for E where E: From<ProtoError> + std::error::Error + Clone {}