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

            
3
use std::sync::Arc;
4

            
5
use crate::DocSource;
6
use futures::task::SpawnError;
7
use thiserror::Error;
8
use tor_error::{ErrorKind, HasKind};
9

            
10
/// An error originated by the directory manager code
11
#[derive(Error, Debug, Clone)]
12
#[non_exhaustive]
13
pub enum Error {
14
    /// We received a document we didn't want at all.
15
    #[error("unwanted object: {0}")]
16
    Unwanted(&'static str),
17
    /// This DirMgr doesn't support downloads.
18
    #[error("tried to download information on a DirMgr with no download support")]
19
    NoDownloadSupport,
20
    /// We couldn't read something from disk that we should have been
21
    /// able to read.
22
    #[error("corrupt cache: {0}")]
23
    CacheCorruption(&'static str),
24
    /// rusqlite gave us an error.
25
    #[error("sqlite error: {0}")]
26
    SqliteError(#[source] Arc<rusqlite::Error>),
27
    /// A schema version that says we can't read it.
28
    #[error("unrecognized data storage schema")]
29
    UnrecognizedSchema,
30
    /// We couldn't configure the network.
31
    #[error("bad network configuration")]
32
    BadNetworkConfig(&'static str),
33
    /// User requested an operation that required a usable
34
    /// bootstrapped directory, but we didn't have one.
35
    #[error("directory not present or not up-to-date")]
36
    DirectoryNotPresent,
37
    /// A consensus document is signed by an unrecognized authority set.
38
    #[error("authorities on consensus do not match what we expect.")]
39
    UnrecognizedAuthorities,
40
    /// A directory manager has been dropped; background tasks can exit too.
41
    #[error("dirmgr has been dropped; background tasks exiting")]
42
    ManagerDropped,
43
    /// We made a bunch of attempts, but weren't unable to advance the
44
    /// state of a download.
45
    #[error("unable to finish bootstrapping a directory")]
46
    CantAdvanceState,
47
    /// Blob storage error
48
    #[error("storage error: {0}")]
49
    StorageError(String),
50
    /// An error given by the consensus diff crate.
51
    #[error("consdiff error: {0}")]
52
    ConsensusDiffError(#[from] tor_consdiff::Error),
53
    /// Invalid UTF8 in directory response.
54
    #[error("invalid utf-8 from directory server")]
55
    BadUtf8FromDirectory(#[source] std::string::FromUtf8Error),
56
    /// Invalid UTF8 from our cache.
57
    #[error("Invalid utf-8 in directory cache")]
58
    BadUtf8InCache(#[source] std::str::Utf8Error),
59
    /// Invalid hexadecimal value in the cache.
60
    #[error("Invalid hexadecimal id in directory cache")]
61
    BadHexInCache(#[source] hex::FromHexError),
62
    /// An error given by the network document crate.
63
    #[error("netdoc error from {source}: {cause}")]
64
    NetDocError {
65
        /// Where the document came from.
66
        source: DocSource,
67
        /// What error we got.
68
        #[source]
69
        cause: tor_netdoc::Error,
70
    },
71
    /// An error given by dirclient
72
    #[error("dirclient error: {0}")]
73
    DirClientError(#[from] tor_dirclient::Error),
74
    /// An error given by the checkable crate.
75
    #[error("checkable error: {0}")]
76
    SignatureError(#[source] Arc<signature::Error>),
77
    /// An IO error occurred while manipulating storage on disk.
78
    #[error("IO error: {0}")]
79
    IOError(#[source] Arc<std::io::Error>),
80
    /// An attempt was made to bootstrap a `DirMgr` created in offline mode.
81
    #[error("cannot bootstrap offline DirMgr")]
82
    OfflineMode,
83

            
84
    /// Unable to spawn task
85
    #[error("unable to spawn {spawning}")]
86
    Spawn {
87
        /// What we were trying to spawn
88
        spawning: &'static str,
89
        /// What happened when we tried to spawn it
90
        #[source]
91
        cause: Arc<SpawnError>,
92
    },
93

            
94
    /// Other error from an external directory provider
95
    #[error("external directory provider")]
96
    ExternalDirProvider {
97
        /// What happened
98
        #[source]
99
        cause: Arc<dyn std::error::Error + Send + Sync + 'static>,
100

            
101
        /// The kind
102
        kind: ErrorKind,
103
    },
104

            
105
    /// A programming problem, either in our code or the code calling it.
106
    #[error("programming problem: {0}")]
107
    Bug(#[from] tor_error::Bug),
108
}
109

            
110
impl From<std::io::Error> for Error {
111
1
    fn from(err: std::io::Error) -> Self {
112
1
        Self::IOError(Arc::new(err))
113
1
    }
114
}
115

            
116
impl From<signature::Error> for Error {
117
    fn from(err: signature::Error) -> Self {
118
        Self::SignatureError(Arc::new(err))
119
    }
120
}
121

            
122
impl Error {
123
    /// Construct a new `Error` from a `SpawnError`.
124
    pub(crate) fn from_spawn(spawning: &'static str, err: SpawnError) -> Error {
125
        Error::Spawn {
126
            spawning,
127
            cause: Arc::new(err),
128
        }
129
    }
130

            
131
    /// Construct a new `Error` from `tor_netdoc::Error`.
132
    ///
133
    /// Also takes a source so that we can keep track of where the document came from.
134
1
    pub(crate) fn from_netdoc(source: DocSource, cause: tor_netdoc::Error) -> Error {
135
1
        Error::NetDocError { source, cause }
136
1
    }
137
}
138

            
139
impl From<rusqlite::Error> for Error {
140
1
    fn from(err: rusqlite::Error) -> Self {
141
1
        use ErrorKind as EK;
142
1
        let kind = sqlite_error_kind(&err);
143
1
        match kind {
144
            EK::Internal | EK::BadApiUsage => {
145
                // TODO: should this be a .is_bug() on EK ?
146
                tor_error::Bug::from_error(kind, err, "sqlite detected bug").into()
147
            }
148
1
            _ => Self::SqliteError(Arc::new(err)),
149
        }
150
1
    }
151
}
152

            
153
impl HasKind for Error {
154
    fn kind(&self) -> ErrorKind {
155
        use Error as E;
156
        use ErrorKind as EK;
157
        match self {
158
            E::Unwanted(_) => EK::TorProtocolViolation,
159
            E::NoDownloadSupport => EK::NotImplemented,
160
            E::CacheCorruption(_) => EK::CacheCorrupted,
161
            E::SqliteError(e) => sqlite_error_kind(e),
162
            E::UnrecognizedSchema => EK::CacheCorrupted,
163
            E::BadNetworkConfig(_) => EK::InvalidConfig,
164
            E::DirectoryNotPresent => EK::DirectoryExpired,
165
            E::BadUtf8FromDirectory(_) => EK::TorProtocolViolation,
166
            E::BadUtf8InCache(_) => EK::CacheCorrupted,
167
            E::BadHexInCache(_) => EK::CacheCorrupted,
168
            E::UnrecognizedAuthorities => EK::TorProtocolViolation,
169
            E::ManagerDropped => EK::ArtiShuttingDown,
170
            E::CantAdvanceState => EK::TorAccessFailed,
171
            E::StorageError(_) => EK::CacheAccessFailed,
172
            E::ConsensusDiffError(_) => EK::TorProtocolViolation,
173
            E::NetDocError { source, .. } => match source {
174
                DocSource::LocalCache => EK::CacheCorrupted,
175
                DocSource::DirServer { .. } => EK::TorProtocolViolation,
176
            },
177
            E::DirClientError(e) => e.kind(),
178
            E::SignatureError(_) => EK::TorProtocolViolation,
179
            E::IOError(_) => EK::CacheAccessFailed,
180
            E::OfflineMode => EK::BadApiUsage,
181
            E::Spawn { cause, .. } => cause.kind(),
182
            E::ExternalDirProvider { kind, .. } => *kind,
183
            E::Bug(e) => e.kind(),
184
        }
185
    }
186
}
187

            
188
/// Convert a sqlite error code into a real ErrorKind.
189
1
fn sqlite_error_kind(e: &rusqlite::Error) -> ErrorKind {
190
1
    use rusqlite::ErrorCode as RE;
191
1
    use ErrorKind as EK;
192
1

            
193
1
    match e {
194
1
        rusqlite::Error::SqliteFailure(code, _) => match code.code {
195
            RE::DatabaseCorrupt => EK::CacheCorrupted,
196
            RE::SchemaChanged
197
            | RE::TooBig
198
            | RE::ConstraintViolation
199
            | RE::TypeMismatch
200
            | RE::ApiMisuse
201
            | RE::NoLargeFileSupport
202
            | RE::ParameterOutOfRange
203
            | RE::OperationInterrupted
204
            | RE::ReadOnly
205
            | RE::OperationAborted
206
            | RE::DatabaseBusy
207
            | RE::DatabaseLocked
208
            | RE::OutOfMemory
209
            | RE::InternalMalfunction => EK::Internal,
210

            
211
            RE::FileLockingProtocolFailed
212
            | RE::AuthorizationForStatementDenied
213
            | RE::NotFound
214
            | RE::DiskFull
215
            | RE::CannotOpen
216
            | RE::SystemIoFailure
217
1
            | RE::PermissionDenied => EK::CacheAccessFailed,
218
            RE::NotADatabase => EK::InvalidConfig,
219
            _ => EK::Internal,
220
        },
221

            
222
        // TODO: Some of the other sqlite error types can sometimes represent
223
        // possible database corruption (like UTF8Error.)  But I haven't
224
        // found a way to distinguish when.
225
        _ => EK::Internal,
226
    }
227
1
}