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
|
//! Errors relating to being a hidden service client
use std::sync::Arc;
use futures::task::SpawnError;
use thiserror::Error;
use tracing::error;
use retry_error::RetryError;
use safelog::Redacted;
use tor_error::{internal, Bug, ErrorKind, ErrorReport as _, HasKind};
use tor_llcrypto::pk::ed25519::Ed25519Identity;
/// Error that occurred attempting to reach a hidden service
#[derive(Error, Clone, Debug)]
#[non_exhaustive]
pub enum ConnError {
/// Invalid hidden service identity (`.onion` address)
#[error("Invalid hidden service identity (`.onion` address)")]
InvalidHsId,
/// Unable to download hidden service descriptor
#[error("Unable to download hidden service descriptor")]
DescriptorDownload(RetryError<tor_error::Report<DescriptorError>>),
/// The consensus network contains no suitable hidden service directories!
#[error("consensus contains no suitable hidden service directories")]
NoHsDirs,
/// Unable to spawn
#[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>,
},
/// Internal error
#[error("{0}")]
Bug(#[from] Bug),
}
/// Error that occurred attempting to download a descriptor
#[derive(Error, Clone, Debug)]
#[non_exhaustive]
#[error("tried hsdir {hsdir}: {error}")]
pub struct DescriptorError {
/// Which hsdir we were trying
// TODO #813 this should be Redacted<RelayDescription> or something
// TODO HS: is even this too much leakage?
// Perhaps the set of redacted hsdir ids may identify the service;
// in that case this should be `Sensitive` instead.
pub hsdir: Redacted<Ed25519Identity>,
/// What happened
#[source]
pub error: DescriptorErrorDetail,
}
// This trivial AsRef impl enables use of `tor_error::Report`
// TODO: It would nice if this could be generated more automatically;
// it's basically `impl AsRef<dyn Trait> for T where T: Trait`.
impl AsRef<dyn std::error::Error + 'static> for DescriptorError {
fn as_ref(&self) -> &(dyn std::error::Error + 'static) {
self as _
}
}
/// Error that occurred attempting to download a descriptor
#[derive(Error, Clone, Debug)]
#[non_exhaustive]
//
// NOTE! These are in an order! "Most interesting" errors come last.
// Specifically, after various attempts, the ErrorKind of the overall error
// will be that of the error which is latest in this enum.
//
#[derive(strum::EnumDiscriminants)]
#[strum_discriminants(derive(PartialOrd, Ord))]
pub enum DescriptorErrorDetail {
/// Timed out
#[error("timed out")]
Timeout,
/// Failed to establish circuit to hidden service directory
#[error("circuit failed")]
Circuit(#[from] tor_circmgr::Error),
/// Failed to establish stream to hidden service directory
#[error("stream failed")]
Stream(#[source] tor_proto::Error),
/// Failed to make directory request
#[error("directory error")]
Directory(#[from] tor_dirclient::RequestError),
/// Failed to parse or validate descriptor
#[error("invalid descriptor")]
InvalidDescriptor(#[from] tor_netdoc::Error),
/// Internal error
#[error("{0}")]
Bug(#[from] Bug),
}
impl HasKind for ConnError {
fn kind(&self) -> ErrorKind {
use ConnError as CE;
use ErrorKind as EK;
match self {
CE::InvalidHsId => EK::InvalidStreamTarget,
CE::NoHsDirs => EK::TorDirectoryUnusable,
CE::Spawn { cause, .. } => cause.kind(),
CE::Bug(e) => e.kind(),
CE::DescriptorDownload(attempts) => attempts
.sources()
.max_by_key(|attempt| DescriptorErrorDetailDiscriminants::from(&attempt.0.error))
.map(|attempt| attempt.0.kind())
.unwrap_or_else(|| {
let bug = internal!("internal error, empty CE::DescriptorDownload");
error!("bug: {}", bug.report());
bug.kind()
}),
}
}
}
impl HasKind for DescriptorError {
fn kind(&self) -> ErrorKind {
self.error.kind()
}
}
impl HasKind for DescriptorErrorDetail {
fn kind(&self) -> ErrorKind {
use tor_dirclient::RequestError as RE;
use tor_netdoc::NetdocErrorKind as NEK;
use DescriptorErrorDetail as DED;
use ErrorKind as EK;
match self {
DED::Timeout => EK::TorNetworkTimeout,
DED::Circuit(e) => e.kind(),
DED::Stream(e) => e.kind(),
DED::Directory(RE::HttpStatus(st)) if *st == 404 => EK::OnionServiceNotFound,
DED::Directory(RE::ResponseTooLong(_)) => EK::OnionServiceProtocolViolation,
DED::Directory(RE::Utf8Encoding(_)) => EK::OnionServiceProtocolViolation,
DED::Directory(other_re) => other_re.kind(),
DED::InvalidDescriptor(e) => match e.netdoc_error_kind() {
NEK::BadTimeBound | NEK::BadSignature => EK::OnionServiceDescriptorValidationFailed,
_ => EK::OnionServiceDescriptorParsingFailed,
},
DED::Bug(e) => e.kind(),
}
}
}
/// Error that occurred attempting to start up a hidden service client connector
#[derive(Error, Clone, Debug)]
#[non_exhaustive]
pub enum StartupError {
/// Internal error
#[error("{0}")]
Bug(#[from] Bug),
}
impl HasKind for StartupError {
fn kind(&self) -> ErrorKind {
use StartupError as SE;
match self {
SE::Bug(e) => e.kind(),
}
}
}
|