tor_ptmgr/
err.rs

1//! Errors to do with pluggable transports.
2
3use fs_mistrust::anon_home::PathExt as _;
4use futures::task::SpawnError;
5use std::path::PathBuf;
6use std::sync::Arc;
7use tor_chanmgr::factory::AbstractPtError;
8use tor_config_path::{CfgPath, CfgPathError};
9use tor_error::{ErrorKind, HasKind, HasRetryTime, RetryTime};
10
11/// An error spawning or managing a pluggable transport.
12#[derive(Clone, Debug, thiserror::Error)]
13#[non_exhaustive]
14pub enum PtError {
15    /// We failed to launch a set of pluggable transports in the provided deadline.
16    #[error("PT launch timed out")]
17    Timeout,
18    /// A PT binary does not support a set of pluggable transports.
19    #[error("PT binary does not support transports: {0:?}")]
20    ClientTransportsUnsupported(Vec<String>),
21    /// A PT binary failed to launch a pluggable transport, and reported
22    /// an error message.
23    #[error("Transport '{}' failed to launch, saying: {:?}", transport, message)]
24    TransportGaveError {
25        /// The transport that failed.
26        transport: String,
27        /// The failure message.
28        message: String,
29    },
30    /// A pluggable transport binary failed to understand us.
31    #[error("PT reported protocol error: {0}")]
32    ChildProtocolViolation(String),
33    /// A pluggable transport binary violated the protocol.
34    #[error("PT violated protocol: {0}")]
35    ProtocolViolation(String),
36    /// A pluggable transport binary doesn't support version 1 of the IPC protocol.
37    #[error("PT binary uses unsupported protocol version")]
38    UnsupportedVersion,
39    /// A pluggable transport binary couldn't use the provided proxy URI.
40    #[error("PT binary failed to use proxy URI: {0}")]
41    ProxyError(String),
42    /// A pluggable transport binary quit or was stopped.
43    #[error("PT binary gone")]
44    ChildGone,
45    /// An error was encountered communicating with a pluggable transport binary. The PT is no
46    /// longer usable.
47    #[error("Failed to read from PT binary: {0}")]
48    ChildReadFailed(Arc<std::io::Error>),
49    /// We couldn't spawn a pluggable transport binary as a child process.
50    #[error("Couldn't execute PT binary at {}: {}", path.anonymize_home(), error)]
51    ChildSpawnFailed {
52        /// The binary path we tried to execute.
53        path: PathBuf,
54        /// The I/O error returned.
55        #[source]
56        error: Arc<std::io::Error>,
57    },
58    /// We failed to parse something a pluggable transport sent us.
59    #[error("Couldn't parse IPC line \"{}\": {}", line, error)]
60    IpcParseFailed {
61        /// The offending line.
62        line: String,
63        /// The error encountered parsing it.
64        error: String,
65    },
66    /// We couldn't create a state directory.
67    #[error("Failed to create a state directory at {}: {}", path.anonymize_home(), error)]
68    StatedirCreateFailed {
69        /// The offending path.
70        path: PathBuf,
71        /// The error encountered.
72        #[source]
73        error: Arc<std::io::Error>,
74    },
75    /// We couldn't expand a path.
76    #[error("Failed to expand path {}: {}", path, error)]
77    PathExpansionFailed {
78        /// The offending path.
79        path: CfgPath,
80        /// The error encountered.
81        #[source]
82        error: CfgPathError,
83    },
84    /// A binary path does not have the syntax of a *file* name.
85    ///
86    /// For example, it ends in a slash, indicating a directory.
87    //
88    // TODO: this should be rejected at the configuration parsing level, and treated as a bug here.
89    #[error("Configured binary path {} doesn't have syntax of a file", path.anonymize_home())]
90    NotAFile {
91        /// The offending path.
92        path: PathBuf,
93    },
94    /// Unable to spawn reactor task.
95    #[error("Unable to spawn reactor task.")]
96    Spawn {
97        /// What happened when we tried to spawn it.
98        #[source]
99        cause: Arc<SpawnError>,
100    },
101    /// The requested transport was found to be missing due to racing with reconfiguration
102    #[error("Transport not found due to concurrent reconfiguration")]
103    // TODO: That this can occur at all is a bug.
104    // See https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/901#note_2858455
105    UnconfiguredTransportDueToConcurrentReconfiguration,
106    /// The pluggable transport reactor failed.
107    #[error("Internal error")]
108    Internal(#[from] tor_error::Bug),
109}
110
111impl HasKind for PtError {
112    fn kind(&self) -> ErrorKind {
113        use ErrorKind as EK;
114        use PtError as E;
115        match self {
116            E::ClientTransportsUnsupported(_) => EK::InvalidConfig,
117            E::ChildProtocolViolation(_)
118            | E::ProtocolViolation(_)
119            | E::UnsupportedVersion
120            | E::IpcParseFailed { .. } => EK::LocalProtocolViolation,
121            E::Timeout
122            | E::TransportGaveError { .. }
123            | E::ChildGone
124            | E::ChildReadFailed(_)
125            | E::ChildSpawnFailed { .. }
126            | E::ProxyError(_) => EK::ExternalToolFailed,
127            E::StatedirCreateFailed { .. } => EK::PersistentStateAccessFailed,
128            E::UnconfiguredTransportDueToConcurrentReconfiguration => EK::TransientFailure,
129            E::PathExpansionFailed { .. } => EK::InvalidConfig,
130            E::NotAFile { .. } => EK::InvalidConfig,
131            E::Internal(e) => e.kind(),
132            E::Spawn { cause, .. } => cause.kind(),
133        }
134    }
135}
136
137impl HasRetryTime for PtError {
138    fn retry_time(&self) -> RetryTime {
139        use PtError as E;
140        use RetryTime as RT;
141        match self {
142            E::ClientTransportsUnsupported(_)
143            | E::ChildProtocolViolation(_)
144            | E::ProtocolViolation(_)
145            | E::IpcParseFailed { .. }
146            | E::NotAFile { .. }
147            | E::UnsupportedVersion
148            | E::Internal(_)
149            | E::Spawn { .. }
150            | E::PathExpansionFailed { .. } => RT::Never,
151            E::StatedirCreateFailed { .. }
152            | E::TransportGaveError { .. }
153            | E::Timeout
154            | E::UnconfiguredTransportDueToConcurrentReconfiguration
155            | E::ProxyError(_)
156            | E::ChildGone
157            | E::ChildReadFailed(_) => RT::AfterWaiting,
158            E::ChildSpawnFailed { error, .. } => {
159                if error.kind() == std::io::ErrorKind::NotFound {
160                    RT::Never
161                } else {
162                    RT::AfterWaiting
163                }
164            }
165        }
166    }
167}
168
169impl AbstractPtError for PtError {}
170
171/// Standard-issue `Result` alias, with [`PtError`].
172pub type Result<T> = std::result::Result<T, PtError>;