use fs_mistrust::anon_home::PathExt as _;
use futures::task::SpawnError;
use std::path::PathBuf;
use std::sync::Arc;
use tor_chanmgr::factory::AbstractPtError;
use tor_config::{CfgPath, CfgPathError};
use tor_error::{ErrorKind, HasKind, HasRetryTime, RetryTime};
#[derive(Clone, Debug, thiserror::Error)]
#[non_exhaustive]
pub enum PtError {
#[error("PT launch timed out")]
Timeout,
#[error("PT binary does not support transports: {0:?}")]
ClientTransportsUnsupported(Vec<String>),
#[error("Transport '{}' failed to launch, saying: {:?}", transport, message)]
ClientTransportGaveError {
transport: String,
message: String,
},
#[error("PT reported protocol error: {0}")]
ChildProtocolViolation(String),
#[error("PT violated protocol: {0}")]
ProtocolViolation(String),
#[error("PT binary uses unsupported protocol version")]
UnsupportedVersion,
#[error("PT binary failed to use proxy URI: {0}")]
ProxyError(String),
#[error("PT binary gone")]
ChildGone,
#[error("Failed to read from PT binary: {0}")]
ChildReadFailed(Arc<std::io::Error>),
#[error("Couldn't execute PT binary at {}: {}", path.anonymize_home(), error)]
ChildSpawnFailed {
path: PathBuf,
#[source]
error: Arc<std::io::Error>,
},
#[error("Couldn't parse IPC line \"{}\": {}", line, error)]
IpcParseFailed {
line: String,
error: String,
},
#[error("Failed to create a state directory at {}: {}", path.anonymize_home(), error)]
StatedirCreateFailed {
path: PathBuf,
#[source]
error: Arc<std::io::Error>,
},
#[error("Failed to expand path {}: {}", path, error)]
PathExpansionFailed {
path: CfgPath,
#[source]
error: CfgPathError,
},
#[error("Configured binary path {} doesn't have syntax of a file", path.anonymize_home())]
NotAFile {
path: PathBuf,
},
#[error("Unable to spawn reactor task.")]
Spawn {
#[source]
cause: Arc<SpawnError>,
},
#[error("Transport not found due to concurrent reconfiguration")]
UnconfiguredTransportDueToConcurrentReconfiguration,
#[error("Internal error")]
Internal(#[from] tor_error::Bug),
}
impl HasKind for PtError {
fn kind(&self) -> ErrorKind {
use ErrorKind as EK;
use PtError as E;
match self {
E::ClientTransportsUnsupported(_) => EK::InvalidConfig,
E::ChildProtocolViolation(_)
| E::ProtocolViolation(_)
| E::UnsupportedVersion
| E::IpcParseFailed { .. } => EK::LocalProtocolViolation,
E::Timeout
| E::ClientTransportGaveError { .. }
| E::ChildGone
| E::ChildReadFailed(_)
| E::ChildSpawnFailed { .. }
| E::ProxyError(_) => EK::ExternalToolFailed,
E::StatedirCreateFailed { .. } => EK::PersistentStateAccessFailed,
E::UnconfiguredTransportDueToConcurrentReconfiguration => EK::TransientFailure,
E::PathExpansionFailed { .. } => EK::InvalidConfig,
E::NotAFile { .. } => EK::InvalidConfig,
E::Internal(e) => e.kind(),
E::Spawn { cause, .. } => cause.kind(),
}
}
}
impl HasRetryTime for PtError {
fn retry_time(&self) -> RetryTime {
use PtError as E;
use RetryTime as RT;
match self {
E::ClientTransportsUnsupported(_)
| E::ChildProtocolViolation(_)
| E::ProtocolViolation(_)
| E::IpcParseFailed { .. }
| E::NotAFile { .. }
| E::UnsupportedVersion
| E::Internal(_)
| E::Spawn { .. }
| E::PathExpansionFailed { .. } => RT::Never,
E::StatedirCreateFailed { .. }
| E::ClientTransportGaveError { .. }
| E::Timeout
| E::UnconfiguredTransportDueToConcurrentReconfiguration
| E::ProxyError(_)
| E::ChildGone
| E::ChildReadFailed(_) => RT::AfterWaiting,
E::ChildSpawnFailed { error, .. } => {
if error.kind() == std::io::ErrorKind::NotFound {
RT::Never
} else {
RT::AfterWaiting
}
}
}
}
}
impl AbstractPtError for PtError {}
pub type Result<T> = std::result::Result<T, PtError>;