1
//! Declare error types for tor-chanmgr
2

            
3
use std::sync::Arc;
4

            
5
use futures::task::SpawnError;
6
use thiserror::Error;
7

            
8
use crate::factory::AbstractPtError;
9
use tor_error::{ErrorKind, internal};
10
use tor_linkspec::{ChanTarget, IntoOwnedChanTarget, LoggedChanTarget};
11
use 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)
17
use safelog::{MaybeSensitive, Sensitive as ChanSensitive};
18

            
19
use crate::transport::proxied::ProxyError;
20

            
21
/// An error returned by a channel manager.
22
#[derive(Debug, Error, Clone)]
23
#[non_exhaustive]
24
pub 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]
139
pub 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

            
149
impl From<std::io::Error> for ConnectError {
150
10
    fn from(e: std::io::Error) -> Self {
151
10
        Self::Direct(Arc::new(e))
152
10
    }
153
}
154

            
155
impl<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

            
161
impl From<tor_linkspec::ByRelayIdsError> for Error {
162
    fn from(_: tor_linkspec::ByRelayIdsError) -> Self {
163
        Error::MissingId
164
    }
165
}
166

            
167
impl From<tor_linkspec::ListByRelayIdsError> for Error {
168
    fn from(_: tor_linkspec::ListByRelayIdsError) -> Self {
169
        Error::MissingId
170
    }
171
}
172

            
173
impl 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

            
200
impl 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

            
211
impl 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

            
222
impl 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

            
268
impl 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
}