use std::sync::Arc;
use futures::task::SpawnError;
use thiserror::Error;
use crate::factory::AbstractPtError;
use tor_error::{ErrorKind, internal};
use tor_linkspec::{ChanTarget, IntoOwnedChanTarget, LoggedChanTarget};
use tor_proto::ClockSkew;
use safelog::{MaybeSensitive, Sensitive as ChanSensitive};
use crate::transport::proxied::ProxyError;
#[derive(Debug, Error, Clone)]
#[non_exhaustive]
pub enum Error {
#[error("Bug: Target was unusable")]
UnusableTarget(#[source] tor_error::Bug),
#[error("Pending channel for {peer} failed to launch")]
PendingFailed {
peer: LoggedChanTarget,
},
#[error("Channel for {peer} timed out")]
ChanTimeout {
peer: LoggedChanTarget,
},
#[error("Protocol error while opening a channel with {peer}")]
Proto {
#[source]
source: tor_proto::Error,
peer: LoggedChanTarget,
clock_skew: Option<ClockSkew>,
},
#[error("Network IO error, or TLS error, in {action}, talking to {peer:?}")]
Io {
peer: MaybeSensitive<tor_proto::peer::PeerAddr>,
action: &'static str,
#[source]
source: Arc<std::io::Error>,
},
#[error("Connection attempt(s) failed: [(address, error)] = {addresses:?}")]
Connect {
addresses: Vec<(ChanSensitive<String>, ConnectError)>,
},
#[error("unable to spawn {spawning}")]
Spawn {
spawning: &'static str,
#[source]
cause: Arc<SpawnError>,
},
#[error("Could not identify relay by identity key")]
MissingId,
#[error("Relay identity keys were only a partial match for what we wanted.")]
IdentityConflict,
#[error("No plugin available for the transport {0}")]
NoSuchTransport(tor_linkspec::TransportId),
#[error("Channel request cancelled or superseded")]
RequestCancelled,
#[error("Pluggable transport error: {0}")]
Pt(#[source] Arc<dyn AbstractPtError>),
#[error("memory quota error")]
Memquota(#[from] tor_memquota::Error),
#[error("Internal error")]
Internal(#[from] tor_error::Bug),
}
#[derive(Clone, Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ConnectError {
#[error("Problem connecting to relay")]
Direct(#[source] Arc<std::io::Error>),
#[error("Problem connecting to relay via a proxy")]
Proxy(#[from] ProxyError),
}
impl From<std::io::Error> for ConnectError {
fn from(e: std::io::Error) -> Self {
Self::Direct(Arc::new(e))
}
}
impl<T> From<std::sync::PoisonError<T>> for Error {
fn from(_: std::sync::PoisonError<T>) -> Error {
Error::Internal(internal!("Thread failed while holding lock"))
}
}
impl From<tor_linkspec::ByRelayIdsError> for Error {
fn from(_: tor_linkspec::ByRelayIdsError) -> Self {
Error::MissingId
}
}
impl From<tor_linkspec::ListByRelayIdsError> for Error {
fn from(_: tor_linkspec::ListByRelayIdsError) -> Self {
Error::MissingId
}
}
impl tor_error::HasKind for Error {
fn kind(&self) -> ErrorKind {
use Error as E;
use ErrorKind as EK;
use tor_proto::Error as ProtoErr;
match self {
E::ChanTimeout { .. }
| E::Io { .. }
| E::Proto {
source: ProtoErr::ChanIoErr(_),
..
} => EK::TorAccessFailed,
E::Spawn { cause, .. } => cause.kind(),
E::Proto { source, .. } => source.kind(),
E::PendingFailed { .. } => EK::TorAccessFailed,
E::NoSuchTransport(_) => EK::InvalidConfig,
E::UnusableTarget(_) | E::Internal(_) => EK::Internal,
E::MissingId => EK::BadApiUsage,
E::IdentityConflict => EK::TorAccessFailed,
E::Connect { .. } => EK::TorAccessFailed,
E::RequestCancelled => EK::TransientFailure,
E::Memquota(e) => e.kind(),
E::Pt(e) => e.kind(),
}
}
}
impl tor_error::HasRetryTime for ConnectError {
fn retry_time(&self) -> tor_error::RetryTime {
match self {
ConnectError::Direct(_) => tor_error::RetryTime::AfterWaiting,
ConnectError::Proxy(e) => e.retry_time(),
}
}
}
impl tor_error::HasKind for ConnectError {
fn kind(&self) -> ErrorKind {
match self {
ConnectError::Direct(_) => ErrorKind::TorAccessFailed,
ConnectError::Proxy(e) => e.kind(),
}
}
}
impl tor_error::HasRetryTime for Error {
fn retry_time(&self) -> tor_error::RetryTime {
use Error as E;
use tor_error::RetryTime as RT;
match self {
E::ChanTimeout { .. } => RT::Immediate,
E::PendingFailed { .. } | E::Proto { .. } | E::Io { .. } => RT::AfterWaiting,
E::Pt(e) => e.retry_time(),
E::Connect { addresses } => {
RT::earliest_approx(addresses.iter().map(|(_, e)| e.retry_time()))
.unwrap_or(RT::AfterWaiting)
}
E::UnusableTarget(_) => RT::Never,
E::IdentityConflict => RT::Never,
E::NoSuchTransport(_) => RT::Never,
E::RequestCancelled => RT::Immediate,
E::Memquota { .. } => RT::AfterWaiting,
E::Spawn { .. } | E::MissingId | E::Internal(_) => RT::Never,
}
}
}
impl Error {
pub(crate) fn from_spawn(spawning: &'static str, err: SpawnError) -> Error {
Error::Spawn {
spawning,
cause: Arc::new(err),
}
}
pub(crate) fn from_proto_no_skew<T: ChanTarget + ?Sized>(
source: tor_proto::Error,
peer: &T,
) -> Self {
Error::Proto {
source,
peer: peer.to_logged(),
clock_skew: None,
}
}
pub fn clock_skew(&self) -> Option<ClockSkew> {
match self {
Error::Proto { clock_skew, .. } => *clock_skew,
_ => None,
}
}
}