Skip to main content

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}