1
//! Declare an error type for the `tor-hsservice` crate.
2

            
3
use crate::internal_prelude::*;
4

            
5
pub use crate::rend_handshake::{EstablishSessionError, IntroRequestError};
6

            
7
/// An error which occurs trying to create and start up an onion service
8
///
9
/// This is only returned by startup methods.
10
/// After the service is created and started,
11
/// we will continue to try keep the service alive,
12
/// retrying things as necessary.
13
#[derive(Clone, Debug, Error)]
14
#[non_exhaustive]
15
pub enum StartupError {
16
    /// A keystore operation failed.
17
    #[error("Keystore error while attempting to {action}")]
18
    Keystore {
19
        /// The action we were trying to perform.
20
        action: &'static str,
21
        /// The underlying error
22
        #[source]
23
        cause: tor_keymgr::Error,
24
    },
25

            
26
    /// Keystore corruption.
27
    #[error("The keystore is unrecoverably corrupt")]
28
    KeystoreCorrupted,
29

            
30
    /// Trouble reading on-disk state
31
    #[error("reading on-disk state")]
32
    // Not #[from] as that might allow call sites that were *storing* during startup
33
    // to accidentally use this variant.  (Such call sites probably shouldn't exist.)
34
    LoadState(#[source] tor_persist::Error),
35

            
36
    /// Unable to access on-disk state
37
    #[error("Unable to access on-disk state")]
38
    StateDirectoryInaccessible(#[source] tor_persist::Error),
39

            
40
    /// Unable to access on-disk state using underlying IO operations
41
    #[error("Unable to access on-disk state: {action} {}", path.display_lossy())]
42
    // TODO ideally we'd like to use StateDirectoryInaccessiblePersist and tor_persist::Error
43
    // for this too, but tor_persist::Error is quite awkward.
44
    StateDirectoryInaccessibleIo {
45
        /// What happened
46
        #[source]
47
        source: Arc<io::Error>,
48

            
49
        /// What filesystem path we were trying to access
50
        path: PathBuf,
51

            
52
        /// What we were trying to do to it
53
        //
54
        // TODO this should be an enum, not a static string, but see above
55
        action: &'static str,
56
    },
57

            
58
    /// Fatal error (during startup)
59
    #[error("fatal error")]
60
    Fatal(#[from] FatalError),
61

            
62
    /// Unable to spawn task
63
    //
64
    // TODO too many types have an open-coded version of FooError::Spawn
65
    // Instead we should:
66
    //  * Have tor_rtcompat provide a SpawnError struct which contains the task identifier
67
    //  * Have tor_rtcompat provide a spawn method that takes an identifier
68
    //    (and which passes that identifier to runtimes that support such a thing,
69
    //    including our own mock spawner)
70
    //  * Change every crate's task spawning and error handling to use the new things
71
    //    (breaking changes to the error type, unless we retain unused compat error variants)
72
    //
73
    // TODO HSS replace this with a conversion to StartupError::Fatal(FatalError::Spawn ) ?
74
    #[error("Unable to spawn {spawning}")]
75
    Spawn {
76
        /// What we were trying to spawn
77
        spawning: &'static str,
78
        /// What happened when we tried to spawn it.
79
        #[source]
80
        cause: Arc<SpawnError>,
81
    },
82

            
83
    /// Tried to launch an onion service that has already been launched.
84
    #[error("Onion service has already been launched")]
85
    AlreadyLaunched,
86
}
87

            
88
impl HasKind for StartupError {
89
    fn kind(&self) -> ErrorKind {
90
        use ErrorKind as EK;
91
        use StartupError as E;
92
        match self {
93
            E::Keystore { cause, .. } => cause.kind(),
94
            E::KeystoreCorrupted => EK::KeystoreCorrupted,
95
            E::Spawn { cause, .. } => cause.kind(),
96
            E::AlreadyLaunched => EK::BadApiUsage,
97
            E::LoadState(e) => e.kind(),
98
            E::StateDirectoryInaccessible(e) => e.kind(),
99
            E::StateDirectoryInaccessibleIo { .. } => EK::PersistentStateAccessFailed,
100
            E::Fatal(e) => e.kind(),
101
        }
102
    }
103
}
104

            
105
impl From<Bug> for StartupError {
106
    fn from(bug: Bug) -> StartupError {
107
        FatalError::from(bug).into()
108
    }
109
}
110

            
111
/// An error which occurs trying to communicate with a particular client.
112
///
113
/// This is returned by `RendRequest::accept` and `StreamRequest::accept`.
114
#[derive(Clone, Debug, Error)]
115
#[non_exhaustive]
116
pub enum ClientError {
117
    /// Failed to process an INTRODUCE2 request.
118
    #[error("Could not process INTRODUCE request")]
119
    BadIntroduce(#[source] IntroRequestError),
120

            
121
    /// Failed to complete a rendezvous request.
122
    #[error("Could not connect rendezvous circuit.")]
123
    EstablishSession(#[source] EstablishSessionError),
124

            
125
    /// Failed to send a CONNECTED message and get a stream.
126
    #[error("Could not accept stream from rendezvous circuit")]
127
    AcceptStream(#[source] tor_proto::Error),
128

            
129
    /// Failed to send a END message and reject a stream.
130
    #[error("Could not reject stream from rendezvous circuit")]
131
    RejectStream(#[source] tor_proto::Error),
132
}
133

            
134
impl HasKind for ClientError {
135
    fn kind(&self) -> ErrorKind {
136
        match self {
137
            ClientError::BadIntroduce(e) => e.kind(),
138
            ClientError::EstablishSession(e) => e.kind(),
139
            ClientError::AcceptStream(e) => e.kind(),
140
            ClientError::RejectStream(e) => e.kind(),
141
        }
142
    }
143
}
144

            
145
/// Latest time to retry a failed IPT store (eg, disk full)
146
//
147
// TODO (#1226): should we make this configurable? Probably not; it's not clear why a
148
// user would want disk failure errors to be retried on any particular interval.
149
// Instead it might make more sense to consider a unified strategy for handling
150
// state errors.
151
const IPT_STORE_RETRY_MAX: Duration = Duration::from_secs(60);
152

            
153
/// An error arising when trying to store introduction points
154
///
155
/// These don't escape the crate, except to be logged.
156
///
157
/// These errors might be fatal, or they might be something we should retry.
158
#[derive(Clone, Debug, Error)]
159
pub(crate) enum IptStoreError {
160
    /// Unable to store introduction points
161
    #[error("Unable to store introduction points")]
162
    Store(#[from] tor_persist::Error),
163

            
164
    /// Fatal error
165
    #[error("Fatal error")]
166
    Fatal(#[from] FatalError),
167
}
168

            
169
impl From<Bug> for IptStoreError {
170
    fn from(bug: Bug) -> IptStoreError {
171
        FatalError::from(bug).into()
172
    }
173
}
174

            
175
impl IptStoreError {
176
    /// Log this error, and report latest time to retry
177
    ///
178
    /// It's OK to retry this earlier, if we are prompted somehow by other work;
179
    /// this is the longest time we should wait, so that we poll periodically
180
    /// to see if the situation has improved.
181
    ///
182
    /// If the operation shouldn't be retried, the problem was a fatal error,
183
    /// which is simply returned.
184
    // TODO: should this be a HasRetryTime impl instead?  But that has different semantics.
185
    pub(crate) fn log_retry_max(self, nick: &HsNickname) -> Result<Duration, FatalError> {
186
        use IptStoreError as ISE;
187
        let wait = match self {
188
            ISE::Store(_) => IPT_STORE_RETRY_MAX,
189
            ISE::Fatal(e) => return Err(e),
190
        };
191
        error_report!(self, "HS service {}: error", nick);
192
        Ok(wait)
193
    }
194
}
195

            
196
/// An error which means we cannot continue to try to operate an onion service.
197
///
198
/// These errors only occur during operation, and only for catastrophic reasons
199
/// (such as the async reactor shutting down).
200
//
201
// TODO where is FatalError emitted from this crate into the wider program ?
202
// Perhaps there will be some kind of monitoring handle (TODO (#1083)) that can produce one of these.
203
#[derive(Clone, Debug, Error)]
204
#[non_exhaustive]
205
pub enum FatalError {
206
    /// Unable to spawn task
207
    #[error("Unable to spawn {spawning}")]
208
    Spawn {
209
        /// What we were trying to spawn
210
        spawning: &'static str,
211
        /// What happened when we tried to spawn it.
212
        #[source]
213
        cause: Arc<SpawnError>,
214
    },
215

            
216
    /// Failed to access the keystore.
217
    #[error("failed to access keystore")]
218
    Keystore(#[from] tor_keymgr::Error),
219

            
220
    /// Failed to access the keystore due to incompatible concurrent access.
221
    ///
222
    /// This can only happen if someone is modifying the contents of the keystore
223
    /// just as we are trying to access it.
224
    #[error("keystore {action} failed for {path} (someone else is writing to the keystore?!)")]
225
    KeystoreRace {
226
        /// What action we were trying to perform
227
        action: &'static str,
228
        /// The ArtiPath we were trying to access
229
        path: tor_keymgr::ArtiPath,
230
    },
231

            
232
    /// The identity keypair of the service could not be found in the keystore.
233
    #[error("Hidden service identity key not found: {0}")]
234
    MissingHsIdKeypair(HsNickname),
235

            
236
    /// IPT keys found for being-created IPT
237
    ///
238
    /// This could only happen if someone is messing with our RNG
239
    /// or our code is completely wrong, or something.
240
    #[error("IPT keys found for being-created IPT {0} (serious key management problems!)")]
241
    IptKeysFoundUnexpectedly(tor_keymgr::ArtiPath),
242

            
243
    /// The network directory provider is shutting down without giving us the
244
    /// netdir we asked for.
245
    #[error("{0}")]
246
    NetdirProviderShutdown(#[from] tor_netdir::NetdirProviderShutdown),
247

            
248
    /// A field was missing when we tried to construct a
249
    /// [`OnionService`](crate::OnionService).
250
    #[error("Missing field when constructing OnionService")]
251
    MissingField(#[from] derive_builder::UninitializedFieldError),
252

            
253
    /// Invalid restricted discovery configuration.
254
    #[error("Restricted discovery is enabled, but no authorized clients are configured. Service will be unreachable")]
255
    #[cfg(feature = "restricted-discovery")]
256
    RestrictedDiscoveryNoClients,
257

            
258
    /// An error caused by a programming issue . or a failure in another
259
    /// library that we can't work around.
260
    #[error("Programming error")]
261
    Bug(#[from] Bug),
262
}
263

            
264
impl FatalError {
265
    /// Construct a new `FatalError` from a `SpawnError`.
266
    //
267
    // TODO lots of our Errors have a function exactly like this.
268
    pub(super) fn from_spawn(spawning: &'static str, err: SpawnError) -> FatalError {
269
        FatalError::Spawn {
270
            spawning,
271
            cause: Arc::new(err),
272
        }
273
    }
274
}
275

            
276
impl HasKind for FatalError {
277
    fn kind(&self) -> ErrorKind {
278
        use ErrorKind as EK;
279
        use FatalError as FE;
280
        match self {
281
            FE::Spawn { cause, .. } => cause.kind(),
282
            FE::Keystore(e) => e.kind(),
283
            FE::MissingHsIdKeypair(_) => EK::Internal, // TODO (#1256) This is not always right.
284
            FE::KeystoreRace { .. } => EK::KeystoreAccessFailed,
285
            FE::IptKeysFoundUnexpectedly(_) => EK::Internal, // This is indeed quite bad.
286
            FE::NetdirProviderShutdown(e) => e.kind(),
287
            FE::MissingField(_) => EK::BadApiUsage,
288
            #[cfg(feature = "restricted-discovery")]
289
            FE::RestrictedDiscoveryNoClients => EK::InvalidConfig,
290
            FE::Bug(e) => e.kind(),
291
        }
292
    }
293
}
294

            
295
/// Error occurring in [`IptManager::expire_old_ipts_external_persistent_state`](crate::ipt_mgr::IptManager::expire_old_ipts_external_persistent_state)
296
///
297
/// All that happens with these errors is that they are logged
298
/// (with a rate limit).
299
#[derive(Error, Clone, Debug)]
300
pub(crate) enum StateExpiryError {
301
    /// Key expiry failed
302
    #[error("key(s)")]
303
    Key(#[from] tor_keymgr::Error),
304
    /// Replay log expiry (or other things using `tor_persist`) failed
305
    #[error("replay log(s): failed to {operation} {}", path.display_lossy())]
306
    ReplayLog {
307
        /// The actual error
308
        #[source]
309
        source: Arc<io::Error>,
310
        /// The pathname
311
        path: PathBuf,
312
        /// What we were doing
313
        operation: &'static str,
314
    },
315
    /// Internal error
316
    #[error("internal error")]
317
    Bug(#[from] Bug),
318
}
319

            
320
impl HasKind for StateExpiryError {
321
    fn kind(&self) -> ErrorKind {
322
        use tor_error::ErrorKind as EK;
323
        use StateExpiryError as SEE;
324
        match self {
325
            SEE::Key(e) => e.kind(),
326
            SEE::ReplayLog { .. } => EK::PersistentStateAccessFailed,
327
            SEE::Bug(e) => e.kind(),
328
        }
329
    }
330
}