1use std::sync::Arc;
3
4use derive_more::{From, Into};
5use futures::task::SpawnError;
6
7use thiserror::Error;
8use tracing::error;
9
10use retry_error::RetryError;
11use safelog::{Redacted, Sensitive};
12use tor_cell::relaycell::hs::IntroduceAckStatus;
13use tor_error::define_asref_dyn_std_error;
14use tor_error::{internal, Bug, ErrorKind, ErrorReport as _, HasKind, HasRetryTime, RetryTime};
15use tor_linkspec::RelayIds;
16use tor_llcrypto::pk::ed25519::Ed25519Identity;
17use tor_netdir::Relay;
18
19pub(crate) type RendPtIdentityForError = Redacted<RelayIds>;
21
22pub(crate) fn rend_pt_identity_for_error(relay: &Relay<'_>) -> RendPtIdentityForError {
24 RelayIds::from_relay_ids(relay).into()
25}
26
27#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, From, Into)]
33#[allow(clippy::exhaustive_structs)]
34#[derive(derive_more::Display)]
35#[display("#{}", self.0 + 1)]
36pub struct IntroPtIndex(pub usize);
37
38#[derive(Error, Clone, Debug)]
40#[non_exhaustive]
41pub enum ConnError {
42 #[error("Invalid hidden service identity (`.onion` address)")]
44 InvalidHsId,
45
46 #[error("Unable to download hidden service descriptor")]
48 DescriptorDownload(RetryError<tor_error::Report<DescriptorError>>),
49
50 #[error("Unable to connect to hidden service using any Rendezvous Point / Introduction Point")]
53 Failed(#[source] RetryError<FailedAttemptError>),
54
55 #[error("consensus contains no suitable hidden service directories")]
57 NoHsDirs,
58
59 #[error("hidden service has no introduction points usable by us")]
63 NoUsableIntroPoints,
64
65 #[error("Unable to spawn {spawning}")]
67 Spawn {
68 spawning: &'static str,
70 #[source]
72 cause: Arc<SpawnError>,
73 },
74
75 #[error("{0}")]
77 Bug(#[from] Bug),
78}
79
80#[derive(Error, Clone, Debug)]
82#[non_exhaustive]
83#[error("tried hsdir {hsdir}: {error}")]
84pub struct DescriptorError {
85 pub hsdir: Sensitive<Ed25519Identity>,
90
91 #[source]
93 pub error: DescriptorErrorDetail,
94}
95define_asref_dyn_std_error!(DescriptorError);
96
97#[derive(Error, Clone, Debug)]
99#[non_exhaustive]
100#[derive(strum::EnumDiscriminants)]
106#[strum_discriminants(derive(PartialOrd, Ord))]
107pub enum DescriptorErrorDetail {
108 #[error("timed out")]
110 Timeout,
111
112 #[error("circuit failed")]
114 Circuit(#[from] tor_circmgr::Error),
115
116 #[error("stream failed")]
118 Stream(#[source] tor_proto::Error),
119
120 #[error("directory error")]
122 Directory(#[from] tor_dirclient::RequestError),
123
124 #[error("problem with descriptor")]
126 Descriptor(#[from] tor_netdoc::doc::hsdesc::HsDescError),
127
128 #[error("{0}")]
130 Bug(#[from] Bug),
131}
132
133#[derive(Error, Clone, Debug)]
135#[non_exhaustive]
136#[derive(strum::EnumDiscriminants)]
142#[strum_discriminants(derive(PartialOrd, Ord))]
143pub enum FailedAttemptError {
145 #[error("Unusable introduction point #{intro_index}")]
147 UnusableIntro {
148 #[source]
150 error: crate::relay_info::InvalidTarget,
151
152 intro_index: IntroPtIndex,
154 },
155
156 #[error("Failed to obtain any circuit to use as a rendezvous circuit")]
158 RendezvousCircuitObtain {
159 #[source]
161 error: tor_circmgr::Error,
162 },
163
164 #[error("Creating a rendezvous circuit and rendezvous point took too long")]
166 RendezvousEstablishTimeout {
167 rend_pt: RendPtIdentityForError,
170 },
171
172 #[error("Failed to establish rendezvous point at {rend_pt}")]
174 RendezvousEstablish {
175 #[source]
177 error: tor_proto::Error,
178
179 rend_pt: RendPtIdentityForError,
182 },
183
184 #[error("Failed to obtain circuit to introduction point {intro_index}")]
186 IntroductionCircuitObtain {
187 #[source]
189 error: tor_circmgr::Error,
190
191 intro_index: IntroPtIndex,
193 },
194
195 #[error("Introduction exchange (with the introduction point) failed")]
197 IntroductionExchange {
198 #[source]
200 error: tor_proto::Error,
201
202 intro_index: IntroPtIndex,
204 },
205
206 #[error("Introduction point reported error in its INTRODUCE_ACK: {status}")]
208 IntroductionFailed {
209 status: IntroduceAckStatus,
211
212 intro_index: IntroPtIndex,
214 },
215
216 #[error("Communication with introduction point {intro_index} took too long")]
221 IntroductionTimeout {
222 intro_index: IntroPtIndex,
224 },
225
226 #[error("Rendezvous at {rend_pt} using introduction point {intro_index} took too long")]
231 RendezvousCompletionTimeout {
232 intro_index: IntroPtIndex,
234
235 rend_pt: RendPtIdentityForError,
238 },
239
240 #[error(
242 "Error on rendezvous circuit when expecting rendezvous completion (RENDEZVOUS2 message)"
243 )]
244 RendezvousCompletionCircuitError {
245 #[source]
247 error: tor_proto::Error,
248
249 intro_index: IntroPtIndex,
251
252 rend_pt: RendPtIdentityForError,
255 },
256
257 #[error("Rendezvous completion end-to-end crypto handshake failed (bad RENDEZVOUS2 message)")]
261 RendezvousCompletionHandshake {
262 #[source]
264 error: tor_proto::Error,
265
266 intro_index: IntroPtIndex,
268
269 rend_pt: RendPtIdentityForError,
272 },
273
274 #[error("{0}")]
276 Bug(#[from] Bug),
277}
278define_asref_dyn_std_error!(FailedAttemptError);
279
280impl FailedAttemptError {
281 pub(crate) fn intro_index(&self) -> Option<IntroPtIndex> {
287 use FailedAttemptError as FAE;
288 match self {
289 FAE::UnusableIntro { intro_index, .. }
290 | FAE::RendezvousCompletionCircuitError { intro_index, .. }
291 | FAE::RendezvousCompletionHandshake { intro_index, .. }
292 | FAE::RendezvousCompletionTimeout { intro_index, .. }
293 | FAE::IntroductionCircuitObtain { intro_index, .. }
294 | FAE::IntroductionExchange { intro_index, .. }
295 | FAE::IntroductionFailed { intro_index, .. }
296 | FAE::IntroductionTimeout { intro_index, .. } => Some(*intro_index),
297 FAE::RendezvousCircuitObtain { .. }
298 | FAE::RendezvousEstablish { .. }
299 | FAE::RendezvousEstablishTimeout { .. }
300 | FAE::Bug(_) => None,
301 }
302 }
303}
304
305impl HasRetryTime for FailedAttemptError {
323 fn retry_time(&self) -> RetryTime {
324 use FailedAttemptError as FAE;
325 use RetryTime as RT;
326 match self {
327 FAE::UnusableIntro { error, .. } => error.retry_time(),
329 FAE::RendezvousCircuitObtain { error } => error.retry_time(),
330 FAE::IntroductionCircuitObtain { error, .. } => error.retry_time(),
331 FAE::IntroductionFailed { status, .. } => status.retry_time(),
332 FAE::RendezvousCompletionCircuitError { error: _e, .. }
334 | FAE::IntroductionExchange { error: _e, .. }
335 | FAE::RendezvousEstablish { error: _e, .. } => RT::AfterWaiting,
336 FAE::RendezvousEstablishTimeout { .. }
338 | FAE::RendezvousCompletionTimeout { .. }
339 | FAE::IntroductionTimeout { .. } => RT::AfterWaiting,
340 FAE::RendezvousCompletionHandshake { error: _e, .. } => RT::Never,
343 FAE::Bug(_) => RT::Never,
344 }
345 }
346}
347
348impl HasKind for ConnError {
349 fn kind(&self) -> ErrorKind {
350 use ConnError as CE;
351 use ErrorKind as EK;
352 match self {
353 CE::InvalidHsId => EK::InvalidStreamTarget,
354 CE::NoHsDirs => EK::TorDirectoryUnusable,
355 CE::NoUsableIntroPoints => EK::OnionServiceProtocolViolation,
356 CE::Spawn { cause, .. } => cause.kind(),
357 CE::Bug(e) => e.kind(),
358
359 CE::DescriptorDownload(attempts) => attempts
360 .sources()
361 .max_by_key(|attempt| DescriptorErrorDetailDiscriminants::from(&attempt.0.error))
362 .map(|attempt| attempt.0.kind())
363 .unwrap_or_else(|| {
364 let bug = internal!("internal error, empty CE::DescriptorDownload");
365 error!("bug: {}", bug.report());
366 bug.kind()
367 }),
368
369 CE::Failed(attempts) => attempts
370 .sources()
371 .max_by_key(|attempt| FailedAttemptErrorDiscriminants::from(*attempt))
372 .map(|attempt| attempt.kind())
373 .unwrap_or_else(|| {
374 let bug = internal!("internal error, empty CE::DescriptorDownload");
375 error!("bug: {}", bug.report());
376 bug.kind()
377 }),
378 }
379 }
380}
381
382impl HasKind for DescriptorError {
383 fn kind(&self) -> ErrorKind {
384 self.error.kind()
385 }
386}
387
388impl HasKind for DescriptorErrorDetail {
389 fn kind(&self) -> ErrorKind {
390 use tor_dirclient::RequestError as RE;
391 use DescriptorErrorDetail as DED;
392 use ErrorKind as EK;
393 match self {
394 DED::Timeout => EK::TorNetworkTimeout,
395 DED::Circuit(e) => e.kind(),
396 DED::Stream(e) => e.kind(),
397 DED::Directory(RE::HttpStatus(st, _)) if *st == 404 => EK::OnionServiceNotFound,
398 DED::Directory(RE::ResponseTooLong(_)) => EK::OnionServiceProtocolViolation,
399 DED::Directory(RE::Utf8Encoding(_)) => EK::OnionServiceProtocolViolation,
400 DED::Directory(other_re) => other_re.kind(),
401 DED::Descriptor(e) => e.kind(),
402 DED::Bug(e) => e.kind(),
403 }
404 }
405}
406
407impl HasKind for FailedAttemptError {
408 fn kind(&self) -> ErrorKind {
409 use ErrorKind as EK;
413 use FailedAttemptError as FAE;
414 match self {
415 FAE::UnusableIntro { .. } => EK::OnionServiceProtocolViolation,
416 FAE::RendezvousCircuitObtain { error, .. } => error.kind(),
417 FAE::RendezvousEstablish { error, .. } => error.kind(),
418 FAE::RendezvousCompletionCircuitError { error, .. } => error.kind(),
419 FAE::RendezvousCompletionHandshake { error, .. } => error.kind(),
420 FAE::RendezvousEstablishTimeout { .. } => EK::TorNetworkTimeout,
421 FAE::IntroductionCircuitObtain { error, .. } => error.kind(),
422 FAE::IntroductionExchange { error, .. } => error.kind(),
423 FAE::IntroductionFailed { .. } => EK::OnionServiceConnectionFailed,
424 FAE::IntroductionTimeout { .. } => EK::TorNetworkTimeout,
425 FAE::RendezvousCompletionTimeout { .. } => EK::RemoteNetworkTimeout,
426 FAE::Bug(e) => e.kind(),
427 }
428 }
429}
430
431#[derive(Error, Clone, Debug)]
433#[non_exhaustive]
434pub enum StartupError {
435 #[error("Unable to spawn {spawning}")]
437 Spawn {
438 spawning: &'static str,
440 #[source]
442 cause: Arc<SpawnError>,
443 },
444
445 #[error("{0}")]
447 Bug(#[from] Bug),
448}
449
450impl HasKind for StartupError {
451 fn kind(&self) -> ErrorKind {
452 use StartupError as SE;
453 match self {
454 SE::Spawn { cause, .. } => cause.kind(),
455 SE::Bug(e) => e.kind(),
456 }
457 }
458}
459
460#[derive(Error, Clone, Debug)]
466#[non_exhaustive]
467pub(crate) enum ProofOfWorkError {
468 #[error("Runtime error from client puzzle solver, #{0}")]
470 Runtime(#[from] tor_hscrypto::pow::RuntimeError),
471
472 #[error("Client puzzle parameters are not valid at this time")]
474 TimeValidity(#[from] tor_checkable::TimeValidityError),
475
476 #[error("Unexpectedly lost contact with solver task")]
478 #[allow(dead_code)]
479 SolverDisconnected,
480}