1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
//! Declare an error type for tor-circmgr
use std::sync::Arc;
use futures::task::SpawnError;
use retry_error::RetryError;
use thiserror::Error;
use tor_error::{Bug, ErrorKind, HasKind};
use tor_linkspec::OwnedChanTarget;
/// An error returned while looking up or building a circuit
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum Error {
/// We started building a circuit on a guard, but later decided not
/// to use that guard.
//
// TODO: We shouldn't count this as an error for the purposes of the number
// of allowable failures of a circuit request.
#[error("Discarded circuit because of speculative guard selection")]
GuardNotUsable,
/// We were waiting on a pending circuit, but it failed to report
#[error("Pending circuit(s) failed without reporting status")]
PendingCanceled,
/// A circuit succeeded, but was cancelled before it could be used.
///
/// Circuits can be cancelled either by a call to
/// `retire_all_circuits()`, or by a configuration change that
/// makes old paths unusable.
//
// TODO: We shouldn't count this as an error for the purposes of the number
// of allowable failures of a circuit request.
#[error("Circuit canceled")]
CircCanceled,
/// A circuit build took too long to finish.
#[error("Circuit took too long to build")]
CircTimeout,
/// A request spent too long waiting for a circuit
#[error("Spent too long waiting for a circuit to build")]
RequestTimeout,
/// No suitable relays for a request
#[error("Can't build path for circuit: {0}")]
NoPath(String),
/// No suitable exit relay for a request.
#[error("Can't find exit for circuit: {0}")]
NoExit(String),
/// Problem creating or updating a guard manager.
#[error("Problem creating or updating guards list: {0}")]
GuardMgr(#[source] tor_guardmgr::GuardMgrError),
/// Problem selecting a guard relay.
#[error("Unable to select a guard relay: {0}")]
Guard(#[from] tor_guardmgr::PickGuardError),
/// Unable to get or build a circuit, despite retrying.
#[error("{0}")]
RequestFailed(RetryError<Box<Error>>),
/// Problem with channel
#[error("Problem with channel to {peer}")]
Channel {
/// Which relay we were trying to connect to
peer: OwnedChanTarget,
/// What went wrong
#[source]
cause: tor_chanmgr::Error,
},
/// Protocol issue while building a circuit.
#[error("Problem building a circuit: {0}")]
Protocol(#[from] tor_proto::Error),
/// We have an expired consensus
#[error("Consensus is expired")]
ExpiredConsensus,
/// Unable to spawn task
#[error("unable to spawn {spawning}")]
Spawn {
/// What we were trying to spawn
spawning: &'static str,
/// What happened when we tried to spawn it.
#[source]
cause: Arc<SpawnError>,
},
/// Problem loading or storing persistent state.
#[error("Problem loading or storing state: {0}")]
State(#[from] tor_persist::Error),
/// An error caused by a programming issue . or a failure in another
/// library that we can't work around.
#[error("Programming issue: {0}")]
Bug(#[from] Bug),
}
impl From<futures::channel::oneshot::Canceled> for Error {
fn from(_: futures::channel::oneshot::Canceled) -> Error {
Error::PendingCanceled
}
}
impl From<tor_rtcompat::TimeoutError> for Error {
fn from(_: tor_rtcompat::TimeoutError) -> Error {
Error::CircTimeout
}
}
impl From<tor_guardmgr::GuardMgrError> for Error {
fn from(err: tor_guardmgr::GuardMgrError) -> Error {
match err {
tor_guardmgr::GuardMgrError::State(e) => Error::State(e),
_ => Error::GuardMgr(err),
}
}
}
impl HasKind for Error {
fn kind(&self) -> ErrorKind {
use Error as E;
use ErrorKind as EK;
match self {
E::Channel { cause, .. } => cause.kind(),
E::Bug(e) => e.kind(),
E::NoPath(_) => EK::NoPath,
E::NoExit(_) => EK::NoExit,
E::PendingCanceled => EK::ReactorShuttingDown,
E::CircTimeout => EK::TorNetworkTimeout,
E::GuardNotUsable => EK::TransientFailure,
E::RequestTimeout => EK::TorNetworkTimeout,
E::RequestFailed(e) => e
.sources()
.max_by_key(|e| e.severity())
.map(|e| e.kind())
.unwrap_or(EK::Internal),
E::CircCanceled => EK::TransientFailure,
E::Protocol(e) => e.kind(),
E::State(e) => e.kind(),
E::GuardMgr(e) => e.kind(),
E::Guard(_) => EK::NoPath,
E::ExpiredConsensus => EK::DirectoryExpired,
E::Spawn { cause, .. } => cause.kind(),
}
}
}
impl Error {
/// Construct a new `Error` from a `SpawnError`.
pub(crate) fn from_spawn(spawning: &'static str, err: SpawnError) -> Error {
Error::Spawn {
spawning,
cause: Arc::new(err),
}
}
/// Return an integer representing the relative severity of this error.
///
/// Used to determine which error to use when determining the kind of a retry error.
fn severity(&self) -> usize {
use Error as E;
match self {
E::GuardNotUsable => 10,
E::PendingCanceled => 20,
E::CircCanceled => 20,
E::CircTimeout => 30,
E::RequestTimeout => 30,
E::NoPath(_) => 40,
E::NoExit(_) => 40,
E::GuardMgr(_) => 40,
E::Guard(_) => 40,
E::RequestFailed(_) => 40,
E::Channel { .. } => 40,
E::Protocol(_) => 45,
E::ExpiredConsensus => 50,
E::Spawn { .. } => 90,
E::State(_) => 90,
E::Bug(_) => 100,
}
}
}
|