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