tor_circmgr/
err.rs

1//! Declare an error type for tor-circmgr
2
3use std::{sync::Arc, time::Instant};
4
5use futures::task::SpawnError;
6use retry_error::RetryError;
7use thiserror::Error;
8
9use oneshot_fused_workaround as oneshot;
10use tor_error::{Bug, ErrorKind, HasKind, HasRetryTime};
11use tor_linkspec::{LoggedChanTarget, OwnedChanTarget};
12use tor_proto::circuit::UniqId;
13
14use crate::mgr::RestrictionFailed;
15
16/// An error returned while looking up or building a circuit
17#[derive(Error, Debug, Clone)]
18#[non_exhaustive]
19pub enum Error {
20    /// We started building a circuit on a guard, but later decided not
21    /// to use that guard.
22    #[error("Discarded circuit {} because of speculative guard selection", _0.display_chan_circ())]
23    GuardNotUsable(UniqId),
24
25    /// We were waiting on a pending circuit, but it failed to report
26    #[error("Pending circuit(s) failed without reporting status")]
27    PendingCanceled,
28
29    /// We were waiting on a pending circuit, but it failed.
30    #[error("Circuit we were waiting for failed to complete")]
31    PendingFailed(#[source] Box<Error>),
32
33    /// We were told that we could use a given circuit, but before we got a
34    /// chance to try it, its usage changed so that we had no longer find
35    /// it suitable.
36    ///
37    /// This is a version of `UsageMismatched` for when a race is the
38    /// likeliest explanation for the mismatch.
39    #[error("Circuit seemed suitable, but another request got it first")]
40    LostUsabilityRace(#[source] RestrictionFailed),
41
42    /// A circuit succeeded, but was cancelled before it could be used.
43    ///
44    /// Circuits can be cancelled either by a call to
45    /// `retire_all_circuits()`, or by a configuration change that
46    /// makes old paths unusable.
47    //
48    // TODO: ideally this would also include the circuit identifier (e.g. its UniqId).
49    // However, this would mean making Error generic over Id,
50    // (this variant is constructed in AbstractCircMgr::do_launch,
51    // where the circuit ID is generic).
52    #[error("Circuit canceled")]
53    CircCanceled,
54
55    /// We were told that we could use a circuit, but when we tried, we found
56    /// that its usage did not support what we wanted.
57    ///
58    /// This can happen due to a race when a number of tasks all decide that
59    /// they can use the same pending circuit at once: one of them will restrict
60    /// the circuit, and the others will get this error.
61    ///
62    /// See `LostUsabilityRace`.
63    #[error("Couldn't apply circuit restriction")]
64    UsageMismatched(#[from] RestrictionFailed),
65
66    /// A circuit build took too long to finish.
67    #[error("Circuit{} took too long to build", OptUniqId(_0))]
68    CircTimeout(Option<UniqId>),
69
70    /// A request spent too long waiting for a circuit
71    #[error("Spent too long trying to construct circuits for this request")]
72    RequestTimeout,
73
74    /// Unable to find a relay in order to build a given path type.
75    #[error("Can't find {role} for {path_kind} circuit: {problem}")]
76    NoRelay {
77        /// The kind of path we were trying to build
78        path_kind: &'static str,
79        /// The kind of relay we were trying to pick
80        role: &'static str,
81        /// The problem we encountered
82        problem: String,
83    },
84
85    /// Problem creating or updating a guard manager.
86    #[error("Problem creating or updating guards list")]
87    GuardMgr(#[source] tor_guardmgr::GuardMgrError),
88
89    /// Problem selecting a guard relay.
90    #[error("Unable to select a guard relay")]
91    Guard(#[from] tor_guardmgr::PickGuardError),
92
93    /// Problem creating a vanguard manager.
94    #[cfg(all(feature = "vanguards", feature = "hs-common"))]
95    #[error("Unable to create vanguard manager")]
96    VanguardMgrInit(#[from] tor_guardmgr::vanguards::VanguardMgrError),
97
98    /// Unable to get or build a circuit, despite retrying.
99    #[error("{0}")]
100    RequestFailed(RetryError<Box<Error>>),
101
102    /// Problem with channel
103    #[error("Problem opening a channel to {peer}")]
104    Channel {
105        /// Which relay we were trying to connect to
106        peer: LoggedChanTarget,
107
108        /// What went wrong
109        #[source]
110        cause: tor_chanmgr::Error,
111    },
112
113    /// Protocol issue while building a circuit.
114    #[error(
115        "Problem building circuit{}, while {}{}",
116        OptUniqId(unique_id),
117        action,
118        WithOptPeer(peer)
119    )]
120    Protocol {
121        /// The action that we were trying to take.
122        action: &'static str,
123        /// The peer that created the protocol error.
124        ///
125        /// This is set to None if we can't blame a single party.
126        peer: Option<LoggedChanTarget>,
127        /// The underlying error.
128        #[source]
129        error: tor_proto::Error,
130        /// The UniqId of the circuit.
131        unique_id: Option<UniqId>,
132    },
133
134    /// Unable to spawn task
135    #[error("Unable to spawn {spawning}")]
136    Spawn {
137        /// What we were trying to spawn
138        spawning: &'static str,
139        /// What happened when we tried to spawn it.
140        #[source]
141        cause: Arc<SpawnError>,
142    },
143
144    /// Problem loading or storing persistent state.
145    #[error("Problem loading or storing state")]
146    State(#[from] tor_persist::Error),
147
148    /// An error caused by a programming issue . or a failure in another
149    /// library that we can't work around.
150    #[error("Programming error")]
151    Bug(#[from] Bug),
152}
153
154tor_error::define_asref_dyn_std_error!(Error);
155tor_error::define_asref_dyn_std_error!(Box<Error>);
156
157impl From<oneshot::Canceled> for Error {
158    fn from(_: oneshot::Canceled) -> Error {
159        Error::PendingCanceled
160    }
161}
162
163impl From<tor_guardmgr::GuardMgrError> for Error {
164    fn from(err: tor_guardmgr::GuardMgrError) -> Error {
165        match err {
166            tor_guardmgr::GuardMgrError::State(e) => Error::State(e),
167            _ => Error::GuardMgr(err),
168        }
169    }
170}
171
172impl HasKind for Error {
173    fn kind(&self) -> ErrorKind {
174        use Error as E;
175        use ErrorKind as EK;
176        match self {
177            E::Channel { cause, .. } => cause.kind(),
178            E::Bug(e) => e.kind(),
179            E::NoRelay { .. } => EK::NoPath,
180            E::PendingCanceled => EK::ReactorShuttingDown,
181            E::PendingFailed(e) => e.kind(),
182            E::CircTimeout(_) => EK::TorNetworkTimeout,
183            E::GuardNotUsable(_) => EK::TransientFailure,
184            E::UsageMismatched(_) => EK::Internal,
185            E::LostUsabilityRace(_) => EK::TransientFailure,
186            E::RequestTimeout => EK::TorNetworkTimeout,
187            E::RequestFailed(errs) => E::summarized_error_kind(errs.sources().map(AsRef::as_ref)),
188            E::CircCanceled => EK::TransientFailure,
189            E::Protocol { error, .. } => error.kind(),
190            E::State(e) => e.kind(),
191            E::GuardMgr(e) => e.kind(),
192            E::Guard(e) => e.kind(),
193            #[cfg(all(feature = "vanguards", feature = "hs-common"))]
194            E::VanguardMgrInit(e) => e.kind(),
195            E::Spawn { cause, .. } => cause.kind(),
196        }
197    }
198}
199
200impl HasRetryTime for Error {
201    fn retry_time(&self) -> tor_error::RetryTime {
202        use tor_error::RetryTime as RT;
203        use Error as E;
204
205        match self {
206            // If we fail because of a timeout, there is no need to wait before trying again.
207            E::CircTimeout(_) | E::RequestTimeout => RT::Immediate,
208
209            // If a circuit that seemed usable was restricted before we got a
210            // chance to try it, that's not our fault: we can try again
211            // immediately.
212            E::LostUsabilityRace(_) => RT::Immediate,
213
214            // If we can't build a path for the usage at all, then retrying
215            // won't help.
216            //
217            // TODO: In some rare cases, these errors can actually happen when
218            // we have walked ourselves into a snag in our path selection.  See
219            // additional "TODO" comments in exitpath.rs.
220            E::NoRelay { .. } => RT::Never,
221
222            // If we encounter UsageMismatched without first converting to
223            // LostUsabilityRace, it reflects a real problem in our code.
224            E::UsageMismatched(_) => RT::Never,
225
226            // These don't reflect a real problem in the circuit building, but
227            // rather mean that we were waiting for something that didn't pan out.
228            // It's okay to try again after a short delay.
229            E::GuardNotUsable(_) | E::PendingCanceled | E::CircCanceled | E::Protocol { .. } => {
230                RT::AfterWaiting
231            }
232
233            // For Channel errors, we can mostly delegate the retry_time decision to
234            // the inner error.
235            //
236            // (We have to handle UnusableTarget specially, since it just means
237            // that we picked a guard or fallback we couldn't use.  A channel to
238            // _that_ target will never succeed, but circuit operations using it
239            // will do fine.)
240            E::Channel {
241                cause: tor_chanmgr::Error::UnusableTarget(_),
242                ..
243            } => RT::AfterWaiting,
244            E::Channel { cause, .. } => cause.retry_time(),
245
246            // These errors are safe to delegate.
247            E::Guard(e) => e.retry_time(),
248            E::PendingFailed(e) => e.retry_time(),
249
250            // When we encounter a bunch of errors, choose the earliest.
251            E::RequestFailed(errors) => {
252                RT::earliest_approx(errors.sources().map(|err| err.retry_time()))
253                    .unwrap_or(RT::Never)
254            }
255
256            #[cfg(all(feature = "vanguards", feature = "hs-common"))]
257            E::VanguardMgrInit(_) => RT::Never,
258
259            // These all indicate an internal error, or an error that shouldn't
260            // be able to happen when we're building a circuit.
261            E::Spawn { .. } | E::GuardMgr(_) | E::State(_) | E::Bug(_) => RT::Never,
262        }
263    }
264
265    fn abs_retry_time<F>(&self, now: Instant, choose_delay: F) -> tor_error::AbsRetryTime
266    where
267        F: FnOnce() -> std::time::Duration,
268    {
269        match self {
270            // We special-case this kind of problem, since we want to choose the
271            // earliest valid retry time.
272            Self::RequestFailed(errors) => tor_error::RetryTime::earliest_absolute(
273                errors.sources().map(|err| err.retry_time()),
274                now,
275                choose_delay,
276            )
277            .unwrap_or(tor_error::AbsRetryTime::Never),
278
279            // For everything else, we just delegate.
280            _ => self.retry_time().absolute(now, choose_delay),
281        }
282    }
283}
284
285impl Error {
286    /// Construct a new `Error` from a `SpawnError`.
287    pub(crate) fn from_spawn(spawning: &'static str, err: SpawnError) -> Error {
288        Error::Spawn {
289            spawning,
290            cause: Arc::new(err),
291        }
292    }
293
294    /// Return an integer representing the relative severity of this error.
295    ///
296    /// Used to determine which error to use when determining the kind of a retry error.
297    fn severity(&self) -> usize {
298        use Error as E;
299        match self {
300            E::GuardNotUsable(_) | E::LostUsabilityRace(_) => 10,
301            E::PendingCanceled => 20,
302            E::CircCanceled => 20,
303            E::CircTimeout(_) => 30,
304            E::RequestTimeout => 30,
305            E::NoRelay { .. } => 40,
306            E::GuardMgr(_) => 40,
307            E::Guard(_) => 40,
308            #[cfg(all(feature = "vanguards", feature = "hs-common"))]
309            E::VanguardMgrInit(_) => 40,
310            E::RequestFailed(_) => 40,
311            E::Channel { .. } => 40,
312            E::Protocol { .. } => 45,
313            E::Spawn { .. } => 90,
314            E::State(_) => 90,
315            E::UsageMismatched(_) => 90,
316            E::Bug(_) => 100,
317            E::PendingFailed(e) => e.severity(),
318        }
319    }
320
321    /// Return true if this error should not count against our total number of
322    /// failures.
323    ///
324    /// We count an error as an "internal reset" if it can happen in normal
325    /// operation and doesn't indicate a real problem with building a circuit, so much as an externally generated "need to retry".
326    pub(crate) fn is_internal_reset(&self) -> bool {
327        match self {
328            // This error is a reset because we expect it to happen while
329            // we're picking guards; if it happens, it means that we now know a
330            // good guard that we should have used instead.
331            Error::GuardNotUsable(_) => true,
332            // This error is a reset because it can only happen on the basis
333            // of a caller action (for example, a decision to reconfigure the
334            // `CircMgr`). If it happens, it just means that we should try again
335            // with the new configuration.
336            Error::CircCanceled => true,
337            // This error is a reset because it doesn't indicate anything wrong
338            // with the circuit: it just means that multiple requests all wanted
339            // to use the circuit at once, and they turned out not to be
340            // compatible with one another after the circuit was built.
341            Error::LostUsabilityRace(_) => true,
342            #[cfg(all(feature = "vanguards", feature = "hs-common"))]
343            Error::VanguardMgrInit(_) => false,
344            Error::PendingCanceled
345            | Error::PendingFailed(_)
346            | Error::UsageMismatched(_)
347            | Error::CircTimeout(_)
348            | Error::RequestTimeout
349            | Error::NoRelay { .. }
350            | Error::GuardMgr(_)
351            | Error::Guard(_)
352            | Error::RequestFailed(_)
353            | Error::Channel { .. }
354            | Error::Protocol { .. }
355            | Error::Spawn { .. }
356            | Error::State(_)
357            | Error::Bug(_) => false,
358        }
359    }
360
361    /// Return a list of the peers to "blame" for this error, if there are any.
362    pub fn peers(&self) -> Vec<&OwnedChanTarget> {
363        match self {
364            Error::RequestFailed(errors) => errors.sources().flat_map(|e| e.peers()).collect(),
365            Error::Channel { peer, .. } => vec![peer.as_inner()],
366            Error::Protocol {
367                peer: Some(peer), ..
368            } => vec![peer.as_inner()],
369            _ => vec![],
370        }
371    }
372
373    /// Given an iterator of errors that have occurred while attempting a single
374    /// failed operation, return the [`ErrorKind`] for the entire attempt.
375    pub fn summarized_error_kind<'a, I>(errs: I) -> ErrorKind
376    where
377        I: Iterator<Item = &'a Error>,
378    {
379        errs.max_by_key(|e| e.severity())
380            .map(|e| e.kind())
381            .unwrap_or(ErrorKind::Internal)
382    }
383}
384
385/// A failure to build any preemptive circuits, with at least one error
386/// condition.
387///
388/// This is a separate type since we never report it outside the crate.
389#[derive(Debug)]
390pub(crate) struct PreemptiveCircError;
391
392/// Helper to display an optional peer, prefixed with the string " with".
393struct WithOptPeer<'a, T>(&'a Option<T>);
394
395impl<'a, T> std::fmt::Display for WithOptPeer<'a, T>
396where
397    T: std::fmt::Display,
398{
399    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
400        if let Some(peer) = self.0.as_ref() {
401            write!(f, " with {}", peer)
402        } else {
403            Ok(())
404        }
405    }
406}
407
408/// Helper to display an optional UniqId.
409struct OptUniqId<'a>(&'a Option<UniqId>);
410
411impl<'a> std::fmt::Display for OptUniqId<'a> {
412    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
413        if let Some(unique_id) = self.0 {
414            write!(f, " {}", unique_id.display_chan_circ())
415        } else {
416            write!(f, "")
417        }
418    }
419}