tor_chanmgr/err.rs
1//! Declare error types for tor-chanmgr
2
3use std::sync::Arc;
4
5use futures::task::SpawnError;
6use thiserror::Error;
7
8use crate::factory::AbstractPtError;
9use tor_error::{ErrorKind, internal};
10use tor_linkspec::{ChanTarget, IntoOwnedChanTarget, LoggedChanTarget};
11use tor_proto::ClockSkew;
12
13// We use "ChanSensitive" for values which are sensitive because they relate to
14// channel-layer trouble, rather than circuit-layer or higher. This will let us find these later:
15// if we want to change `LoggedChanTarget` to `Redacted` (say), we should change these too.
16// (`Redacted` like https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/882)
17use safelog::{MaybeSensitive, Sensitive as ChanSensitive};
18
19use crate::transport::proxied::ProxyError;
20
21/// An error returned by a channel manager.
22#[derive(Debug, Error, Clone)]
23#[non_exhaustive]
24pub enum Error {
25 /// A ChanTarget was given for which no channel could be built.
26 #[error("Bug: Target was unusable")]
27 UnusableTarget(#[source] tor_error::Bug),
28
29 /// We were waiting on a pending channel, but it didn't succeed.
30 #[error("Pending channel for {peer} failed to launch")]
31 PendingFailed {
32 /// Who we were talking to
33 peer: LoggedChanTarget,
34 },
35
36 /// It took too long for us to establish this connection.
37 #[error("Channel for {peer} timed out")]
38 ChanTimeout {
39 /// Who we were trying to talk to
40 peer: LoggedChanTarget,
41 },
42
43 /// A protocol error while making a channel
44 #[error("Protocol error while opening a channel with {peer}")]
45 Proto {
46 /// The underlying error
47 #[source]
48 source: tor_proto::Error,
49 /// Who we were trying to talk to
50 peer: LoggedChanTarget,
51 /// An authenticated ClockSkew (if available) that we received from the
52 /// peer.
53 clock_skew: Option<ClockSkew>,
54 },
55
56 /// Network IO error or TLS error
57 #[error("Network IO error, or TLS error, in {action}, talking to {peer:?}")]
58 Io {
59 /// Who we were talking to
60 peer: MaybeSensitive<tor_proto::peer::PeerAddr>,
61
62 /// What we were doing
63 action: &'static str,
64
65 /// What happened. Might be some TLS library error wrapped up in io::Error
66 #[source]
67 source: Arc<std::io::Error>,
68 },
69
70 /// Failed to build a channel, after trying multiple addresses.
71 //
72 // TODO: This output does not conform to our usual standards.
73 // We should use RetryError and make sure our error_report code can deal with it.
74 #[error("Connection attempt(s) failed: [(address, error)] = {addresses:?}")]
75 Connect {
76 /// The list of addresses we tried to connect to, coupled with
77 /// the error we encountered connecting to each one.
78 ///
79 /// These addresses are currently represented as strings; this
80 /// type may change in the future if and when refactor this error type.
81 addresses: Vec<(ChanSensitive<String>, ConnectError)>,
82 },
83
84 /// Unable to spawn task
85 #[error("unable to spawn {spawning}")]
86 Spawn {
87 /// What we were trying to spawn.
88 spawning: &'static str,
89 /// What happened when we tried to spawn it.
90 #[source]
91 cause: Arc<SpawnError>,
92 },
93
94 /// A relay did not have the set of identity keys that we expected.
95 ///
96 /// (Currently, `tor-chanmgr` only works on relays that have at least
97 /// one recognized identity key.)
98 #[error("Could not identify relay by identity key")]
99 MissingId,
100
101 /// A successful relay channel had one of the identity keys we wanted,
102 /// but not the other(s).
103 ///
104 /// This means that (assuming the relay is well behaved), we will not
105 /// find the ID combination we want.
106 #[error("Relay identity keys were only a partial match for what we wanted.")]
107 IdentityConflict,
108
109 /// Tried to connect via a transport that we don't support.
110 #[error("No plugin available for the transport {0}")]
111 NoSuchTransport(tor_linkspec::TransportId),
112
113 /// An attempt to open a channel failed because it was cancelled or
114 /// superseded by another request or configuration change.
115 #[error("Channel request cancelled or superseded")]
116 RequestCancelled,
117
118 /// An error occurred in a pluggable transport manager.
119 ///
120 /// We can't know the type, because any pluggable transport manager implementing
121 /// `AbstractPtMgr` can be used.
122 /// However, if you're using Arti in the standard configuration, this will be
123 /// `tor-ptmgr`'s `PtError`.
124 #[error("Pluggable transport error: {0}")]
125 Pt(#[source] Arc<dyn AbstractPtError>),
126
127 /// Memory quota error
128 #[error("memory quota error")]
129 Memquota(#[from] tor_memquota::Error),
130
131 /// An internal error of some kind that should never occur.
132 #[error("Internal error")]
133 Internal(#[from] tor_error::Bug),
134}
135
136/// An error trying to open a network connection.
137#[derive(Clone, Debug, thiserror::Error)]
138#[non_exhaustive]
139pub enum ConnectError {
140 /// Unable to open a direct connection.
141 #[error("Problem connecting to relay")]
142 Direct(#[source] Arc<std::io::Error>),
143
144 /// Unable to open a proxied connection.
145 #[error("Problem connecting to relay via a proxy")]
146 Proxy(#[from] ProxyError),
147}
148
149impl From<std::io::Error> for ConnectError {
150 fn from(e: std::io::Error) -> Self {
151 Self::Direct(Arc::new(e))
152 }
153}
154
155impl<T> From<std::sync::PoisonError<T>> for Error {
156 fn from(_: std::sync::PoisonError<T>) -> Error {
157 Error::Internal(internal!("Thread failed while holding lock"))
158 }
159}
160
161impl From<tor_linkspec::ByRelayIdsError> for Error {
162 fn from(_: tor_linkspec::ByRelayIdsError) -> Self {
163 Error::MissingId
164 }
165}
166
167impl From<tor_linkspec::ListByRelayIdsError> for Error {
168 fn from(_: tor_linkspec::ListByRelayIdsError) -> Self {
169 Error::MissingId
170 }
171}
172
173impl tor_error::HasKind for Error {
174 fn kind(&self) -> ErrorKind {
175 use Error as E;
176 use ErrorKind as EK;
177 use tor_proto::Error as ProtoErr;
178 match self {
179 E::ChanTimeout { .. }
180 | E::Io { .. }
181 | E::Proto {
182 source: ProtoErr::ChanIoErr(_),
183 ..
184 } => EK::TorAccessFailed,
185 E::Spawn { cause, .. } => cause.kind(),
186 E::Proto { source, .. } => source.kind(),
187 E::PendingFailed { .. } => EK::TorAccessFailed,
188 E::NoSuchTransport(_) => EK::InvalidConfig,
189 E::UnusableTarget(_) | E::Internal(_) => EK::Internal,
190 E::MissingId => EK::BadApiUsage,
191 E::IdentityConflict => EK::TorAccessFailed,
192 E::Connect { .. } => EK::TorAccessFailed,
193 E::RequestCancelled => EK::TransientFailure,
194 E::Memquota(e) => e.kind(),
195 E::Pt(e) => e.kind(),
196 }
197 }
198}
199
200impl tor_error::HasRetryTime for ConnectError {
201 fn retry_time(&self) -> tor_error::RetryTime {
202 match self {
203 // TODO: Someday we might want to distinguish among different kinds
204 // of IO errors.
205 ConnectError::Direct(_) => tor_error::RetryTime::AfterWaiting,
206 ConnectError::Proxy(e) => e.retry_time(),
207 }
208 }
209}
210
211impl tor_error::HasKind for ConnectError {
212 fn kind(&self) -> ErrorKind {
213 match self {
214 // TODO: Someday we might want to distinguish among different kinds
215 // of IO errors.
216 ConnectError::Direct(_) => ErrorKind::TorAccessFailed,
217 ConnectError::Proxy(e) => e.kind(),
218 }
219 }
220}
221
222impl tor_error::HasRetryTime for Error {
223 fn retry_time(&self) -> tor_error::RetryTime {
224 use Error as E;
225 use tor_error::RetryTime as RT;
226 match self {
227 // We can retry this action immediately; there was already a time delay.
228 E::ChanTimeout { .. } => RT::Immediate,
229
230 // These are worth retrying in a little while.
231 //
232 // TODO: Someday we might want to distinguish among different kinds of IO
233 // errors.
234 E::PendingFailed { .. } | E::Proto { .. } | E::Io { .. } => RT::AfterWaiting,
235
236 // Delegate.
237 E::Pt(e) => e.retry_time(),
238
239 // This error reflects multiple attempts, so we go with whichever error
240 // can be retried the earliest.
241 E::Connect { addresses } => {
242 RT::earliest_approx(addresses.iter().map(|(_, e)| e.retry_time()))
243 .unwrap_or(RT::AfterWaiting)
244 }
245
246 // This one can't succeed: if the ChanTarget have addresses to begin with,
247 // it won't have addresses in the future.
248 E::UnusableTarget(_) => RT::Never,
249
250 // This can't succeed until the relay is reconfigured.
251 E::IdentityConflict => RT::Never,
252
253 // This one can't succeed until the bridge, or our set of
254 // transports, is reconfigured.
255 E::NoSuchTransport(_) => RT::Never,
256
257 E::RequestCancelled => RT::Immediate,
258
259 // Hopefully the problem will pass!
260 E::Memquota { .. } => RT::AfterWaiting,
261
262 // These aren't recoverable at all.
263 E::Spawn { .. } | E::MissingId | E::Internal(_) => RT::Never,
264 }
265 }
266}
267
268impl Error {
269 /// Construct a new `Error` from a `SpawnError`.
270 pub(crate) fn from_spawn(spawning: &'static str, err: SpawnError) -> Error {
271 Error::Spawn {
272 spawning,
273 cause: Arc::new(err),
274 }
275 }
276
277 /// Construct a new `Error` from a `tor_proto::Error`, with no additional
278 /// clock skew information.
279 ///
280 /// This is not an `Into` implementation because we don't want to call it
281 /// accidentally when we actually do have clock skew information.
282 pub(crate) fn from_proto_no_skew<T: ChanTarget + ?Sized>(
283 source: tor_proto::Error,
284 peer: &T,
285 ) -> Self {
286 Error::Proto {
287 source,
288 peer: peer.to_logged(),
289 clock_skew: None,
290 }
291 }
292
293 /// Return the clock skew information from this error (or from an internal
294 /// error).
295 ///
296 /// Only returns the clock skew information if it is authenticated.
297 pub fn clock_skew(&self) -> Option<ClockSkew> {
298 match self {
299 Error::Proto { clock_skew, .. } => *clock_skew,
300 _ => None,
301 }
302 }
303}