1use std::error::Error as StdError;
4use std::sync::Arc;
5
6use crate::DocSource;
7use fs_mistrust::anon_home::PathExt as _;
8use futures::task::SpawnError;
9use thiserror::Error;
10use tor_error::{ErrorKind, HasKind};
11use tor_persist::FsMistrustErrorExt as _;
12
13#[derive(Error, Debug, Clone)]
15#[non_exhaustive]
16pub enum Error {
17 #[error("Received an object we didn't ask for: {0}")]
19 Unwanted(&'static str),
20 #[error("Downloaded netdir is older than the one we have")]
22 NetDirOlder,
23 #[error("Tried to download information on a DirMgr with no download support")]
25 NoDownloadSupport,
26 #[error("Corrupt cache: {0}")]
29 CacheCorruption(&'static str),
30 #[error("Error from sqlite database")]
32 SqliteError(#[source] Arc<rusqlite::Error>),
33 #[error("Failed to create read-only store")]
35 ReadOnlyStorage(#[from] ReadOnlyStorageError),
36 #[error("Unrecognized data storage schema v{schema}. (We support v{supported})")]
38 UnrecognizedSchema {
39 schema: u32,
41 supported: u32,
43 },
44 #[error("Directory not present or not up-to-date")]
47 DirectoryNotPresent,
48 #[error("Authorities on consensus are not the ones we expect")]
50 UnrecognizedAuthorities,
51 #[error("Dirmgr has been dropped; background tasks exiting")]
53 ManagerDropped,
54 #[error("Unable to finish bootstrapping a directory")]
57 CantAdvanceState,
58 #[error("Unable to access lock file")]
60 LockFile(Arc<std::io::Error>),
61 #[error("Error while {action} cache file {}", fname.anonymize_home())]
63 CacheFile {
64 action: &'static str,
66 fname: std::path::PathBuf,
68 #[source]
70 error: Arc<std::io::Error>,
71 },
72 #[error("Problem applying consensus diff")]
74 ConsensusDiffError(#[from] tor_consdiff::Error),
75 #[error("Invalid utf-8 from directory server")]
77 BadUtf8FromDirectory(#[source] std::string::FromUtf8Error),
78 #[error("Invalid utf-8 in directory cache")]
80 BadUtf8InCache(#[source] std::str::Utf8Error),
81 #[error("Invalid hexadecimal id in directory cache")]
83 BadHexInCache(#[source] hex::FromHexError),
84 #[error("Invalid JSON in directory cache")]
86 BadJsonInCache(#[source] Arc<serde_json::Error>),
87 #[error("Invalid document from {source}")]
89 NetDocError {
90 source: DocSource,
92 #[source]
94 cause: tor_netdoc::Error,
95 },
96 #[error("Could not validate consensus from {source}")]
102 ConsensusInvalid {
103 source: DocSource,
105 #[source]
107 cause: tor_netdoc::Error,
108 },
109 #[error("Directory object expired or not yet valid")]
111 UntimelyObject(#[from] tor_checkable::TimeValidityError),
112 #[error("Problem downloading directory object")]
114 DirClientError(#[from] tor_dirclient::Error),
115 #[error("Invalid signatures")]
117 SignatureError(#[source] Arc<signature::Error>),
118 #[error("Tried to bootstrap a DirMgr that was configured as offline-only")]
120 OfflineMode,
121 #[error("Problem accessing cache directory")]
123 CacheAccess(#[from] fs_mistrust::Error),
124 #[error("Problem accessing cache directory")]
129 #[deprecated = "use Error::CacheAccess instead"]
130 CachePermissions(#[source] fs_mistrust::Error),
131 #[error("Unable to spawn {spawning}")]
133 Spawn {
134 spawning: &'static str,
136 #[source]
138 cause: Arc<SpawnError>,
139 },
140
141 #[error("Error from external directory provider")]
143 ExternalDirProvider {
144 #[source]
146 cause: Arc<dyn std::error::Error + Send + Sync + 'static>,
147
148 kind: ErrorKind,
150 },
151
152 #[error("Internal programming issue")]
154 Bug(#[from] tor_error::Bug),
155}
156
157impl From<signature::Error> for Error {
158 fn from(err: signature::Error) -> Self {
159 Self::SignatureError(Arc::new(err))
160 }
161}
162
163impl From<tor_rtcompat::scheduler::SleepError> for Error {
164 fn from(err: tor_rtcompat::scheduler::SleepError) -> Self {
165 use tor_rtcompat::scheduler::SleepError::*;
166 match err {
167 ScheduleDropped => Error::ManagerDropped,
168 e => tor_error::into_internal!("Unexpected sleep error")(e).into(),
169 }
170 }
171}
172
173impl AsRef<dyn StdError + 'static> for Error {
174 fn as_ref(&self) -> &(dyn StdError + 'static) {
175 self
176 }
177}
178
179#[derive(Copy, Clone, Debug)]
181pub(crate) enum BootstrapAction {
182 Nonfatal,
185 Reset,
192 Fatal,
197}
198
199impl Error {
200 pub(crate) fn from_spawn(spawning: &'static str, err: SpawnError) -> Error {
202 Error::Spawn {
203 spawning,
204 cause: Arc::new(err),
205 }
206 }
207
208 pub(crate) fn from_netdoc(source: DocSource, cause: tor_netdoc::Error) -> Error {
212 Error::NetDocError { source, cause }
213 }
214
215 pub(crate) fn from_lockfile(err: std::io::Error) -> Error {
218 Error::LockFile(Arc::new(err))
219 }
220
221 pub(crate) fn indicates_cache_failure(&self) -> bool {
224 #[allow(deprecated)]
225 match self {
226 Error::Unwanted(_)
228 | Error::UnrecognizedAuthorities
229 | Error::BadUtf8FromDirectory(_)
230 | Error::ConsensusDiffError(_)
231 | Error::SignatureError(_)
232 | Error::ConsensusInvalid { .. }
233 | Error::UntimelyObject(_) => true,
234
235 Error::NoDownloadSupport
237 | Error::CacheCorruption(_)
238 | Error::CachePermissions(_)
239 | Error::CacheAccess(_)
240 | Error::SqliteError(_)
241 | Error::ReadOnlyStorage(_)
242 | Error::UnrecognizedSchema { .. }
243 | Error::DirectoryNotPresent
244 | Error::ManagerDropped
245 | Error::CantAdvanceState
246 | Error::LockFile { .. }
247 | Error::CacheFile { .. }
248 | Error::BadUtf8InCache(_)
249 | Error::BadHexInCache(_)
250 | Error::OfflineMode
251 | Error::Spawn { .. }
252 | Error::NetDirOlder
253 | Error::BadJsonInCache(_)
254 | Error::Bug(_) => false,
255
256 Error::DirClientError(e) => e.should_retire_circ(),
258
259 Error::NetDocError { .. } => true,
269
270 Error::ExternalDirProvider { .. } => false,
272 }
273 }
274
275 pub(crate) fn responsible_cache(&self) -> Option<&tor_dirclient::SourceInfo> {
278 match self {
279 Error::NetDocError {
280 source: DocSource::DirServer { source },
281 ..
282 } => source.as_ref(),
283 Error::ConsensusInvalid {
284 source: DocSource::DirServer { source },
285 ..
286 } => source.as_ref(),
287 _ => None,
288 }
289 }
290
291 #[allow(dead_code)]
294 pub(crate) fn bootstrap_action(&self) -> BootstrapAction {
295 #[allow(deprecated)]
296 match self {
297 Error::Unwanted(_)
298 | Error::NetDirOlder
299 | Error::UnrecognizedAuthorities
300 | Error::ConsensusDiffError(_)
301 | Error::BadUtf8FromDirectory(_)
302 | Error::UntimelyObject(_)
303 | Error::DirClientError(_)
304 | Error::SignatureError(_)
305 | Error::NetDocError { .. } => BootstrapAction::Nonfatal,
306
307 Error::ConsensusInvalid { .. } | Error::CantAdvanceState => BootstrapAction::Reset,
308
309 Error::NoDownloadSupport
310 | Error::OfflineMode
311 | Error::CacheCorruption(_)
312 | Error::SqliteError(_)
313 | Error::ReadOnlyStorage(_)
314 | Error::UnrecognizedSchema { .. }
315 | Error::ManagerDropped
316 | Error::LockFile { .. }
317 | Error::CacheFile { .. }
318 | Error::BadUtf8InCache(_)
319 | Error::BadHexInCache(_)
320 | Error::CachePermissions(_)
321 | Error::CacheAccess(_)
322 | Error::Spawn { .. }
323 | Error::BadJsonInCache(_)
324 | Error::ExternalDirProvider { .. } => BootstrapAction::Fatal,
325
326 Error::DirectoryNotPresent | Error::Bug(_) => BootstrapAction::Fatal,
328 }
329 }
330}
331
332impl From<rusqlite::Error> for Error {
333 fn from(err: rusqlite::Error) -> Self {
334 use ErrorKind as EK;
335 let kind = sqlite_error_kind(&err);
336 match kind {
337 EK::Internal | EK::BadApiUsage => {
338 tor_error::Bug::from_error(kind, err, "sqlite detected bug").into()
340 }
341 _ => Self::SqliteError(Arc::new(err)),
342 }
343 }
344}
345
346impl HasKind for Error {
347 fn kind(&self) -> ErrorKind {
348 use Error as E;
349 use ErrorKind as EK;
350 #[allow(deprecated)]
351 match self {
352 E::Unwanted(_) => EK::TorProtocolViolation,
353 E::NoDownloadSupport => EK::NotImplemented,
354 E::CacheCorruption(_) => EK::CacheCorrupted,
355 E::CachePermissions(e) => e.cache_error_kind(),
356 E::CacheAccess(e) => e.cache_error_kind(),
357 E::SqliteError(e) => sqlite_error_kind(e),
358 E::ReadOnlyStorage(_) => EK::LocalResourceAlreadyInUse,
359 E::UnrecognizedSchema { .. } => EK::CacheCorrupted,
360 E::BadJsonInCache(_) => EK::CacheCorrupted,
361 E::DirectoryNotPresent => EK::DirectoryExpired,
362 E::NetDirOlder => EK::TorDirectoryError,
363 E::BadUtf8FromDirectory(_) => EK::TorProtocolViolation,
364 E::BadUtf8InCache(_) => EK::CacheCorrupted,
365 E::BadHexInCache(_) => EK::CacheCorrupted,
366 E::UnrecognizedAuthorities => EK::TorProtocolViolation,
367 E::ManagerDropped => EK::ArtiShuttingDown,
368 E::CantAdvanceState => EK::TorAccessFailed,
369 E::LockFile { .. } => EK::CacheAccessFailed,
370 E::CacheFile { .. } => EK::CacheAccessFailed,
371 E::ConsensusDiffError(_) => EK::TorProtocolViolation,
372 E::NetDocError { source, .. } => match source {
373 DocSource::LocalCache => EK::CacheCorrupted,
374 DocSource::DirServer { .. } => EK::TorProtocolViolation,
375 },
376 E::ConsensusInvalid { source, .. } => match source {
377 DocSource::LocalCache => EK::CacheCorrupted,
378 DocSource::DirServer { .. } => EK::TorProtocolViolation,
379 },
380 E::UntimelyObject(_) => EK::TorProtocolViolation,
381 E::DirClientError(e) => e.kind(),
382 E::SignatureError(_) => EK::TorProtocolViolation,
383 E::OfflineMode => EK::BadApiUsage,
384 E::Spawn { cause, .. } => cause.kind(),
385 E::ExternalDirProvider { kind, .. } => *kind,
386 E::Bug(e) => e.kind(),
387 }
388 }
389}
390
391fn sqlite_error_kind(e: &rusqlite::Error) -> ErrorKind {
393 use rusqlite::ErrorCode as RE;
394 use ErrorKind as EK;
395
396 match e {
397 rusqlite::Error::SqliteFailure(code, _) => match code.code {
398 RE::DatabaseCorrupt => EK::CacheCorrupted,
399 RE::SchemaChanged
400 | RE::TooBig
401 | RE::ConstraintViolation
402 | RE::TypeMismatch
403 | RE::ApiMisuse
404 | RE::NoLargeFileSupport
405 | RE::ParameterOutOfRange
406 | RE::OperationInterrupted
407 | RE::ReadOnly
408 | RE::OperationAborted
409 | RE::DatabaseBusy
410 | RE::DatabaseLocked
411 | RE::OutOfMemory
412 | RE::InternalMalfunction => EK::Internal,
413
414 RE::FileLockingProtocolFailed
415 | RE::AuthorizationForStatementDenied
416 | RE::NotFound
417 | RE::DiskFull
418 | RE::CannotOpen
419 | RE::SystemIoFailure
420 | RE::PermissionDenied => EK::CacheAccessFailed,
421 RE::NotADatabase => EK::InvalidConfig,
422 _ => EK::Internal,
423 },
424
425 _ => EK::Internal,
429 }
430}
431
432#[derive(Error, Debug, Clone)]
434#[non_exhaustive]
435pub enum ReadOnlyStorageError {
436 #[error("The database could not be found.")]
438 NoDatabase,
439
440 #[error("Incompatible data storage schema v{schema}. (We expected v{supported})")]
442 IncompatibleSchema {
443 schema: u32,
445 supported: u32,
447 },
448}