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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
//! Define an error type for the tor-proto crate.
use std::{sync::Arc, time::Duration};
use thiserror::Error;
use tor_cell::relaycell::{msg::EndReason, StreamId};
use tor_error::{ErrorKind, HasKind};
use tor_linkspec::RelayIdType;

/// An error type for the tor-proto crate.
///
/// This type should probably be split into several.  There's more
/// than one kind of error that can occur while doing something with
/// the Tor protocol.
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum Error {
    /// An error that occurred in the tor_bytes crate while decoding an
    /// object.
    #[error("Unable to parse {object}")]
    BytesErr {
        /// What we were trying to parse.
        object: &'static str,
        /// The error that occurred while parsing it.
        #[source]
        err: tor_bytes::Error,
    },
    /// An error that occurred from the io system when using a
    /// channel.
    #[error("IO error on channel with peer")]
    ChanIoErr(#[source] Arc<std::io::Error>),
    /// An error from the io system that occurred when trying to connect a channel.
    #[error("IO error while handshaking with peer")]
    HandshakeIoErr(#[source] Arc<std::io::Error>),
    /// An error occurred while trying to create or encode a cell.
    #[error("Unable to generate or encode {object}")]
    CellEncodeErr {
        /// The object we were trying to create or encode.
        object: &'static str,
        /// The error that occurred.
        #[source]
        err: tor_cell::Error,
    },
    /// An error occurred while trying to decode or parse a cell.
    #[error("Error while parsing {object}")]
    CellDecodeErr {
        /// The object we were trying to decode.
        object: &'static str,
        /// The error that occurred.
        #[source]
        err: tor_cell::Error,
    },
    /// An error occurred while trying to create or encode some non-cell
    /// message.
    ///
    /// This is likely the result of a bug: either in this crate, or the code
    /// that provided the input.
    #[error("Problem while encoding {object}")]
    EncodeErr {
        /// What we were trying to create or encode.
        object: &'static str,
        /// The error that occurred.
        #[source]
        err: tor_bytes::EncodeError,
    },
    /// We found a problem with one of the certificates in the channel
    /// handshake.
    #[error("Problem with certificate on handshake")]
    HandshakeCertErr(#[source] tor_cert::CertError),
    /// We tried to produce too much output for a key derivation function.
    #[error("Tried to extract too many bytes from a KDF")]
    InvalidKDFOutputLength,
    /// We tried to encrypt a message to a hop that wasn't there.
    #[error("Tried to encrypt a cell for a nonexistent hop")]
    NoSuchHop,
    /// The authentication information on this cell was completely wrong,
    /// or the cell was corrupted.
    #[error("Bad relay cell authentication")]
    BadCellAuth,
    /// A circuit-extension handshake failed due to a mismatched authentication
    /// value.
    #[error("Circuit-extension handshake authentication failed")]
    BadCircHandshakeAuth,
    /// Handshake protocol violation.
    #[error("Handshake protocol violation: {0}")]
    HandshakeProto(String),
    /// Handshake broken, maybe due to clock skew.
    ///
    /// (If the problem can't be due to clock skew, we return HandshakeProto
    /// instead.)
    #[error("Handshake failed due to expired certificates (possible clock skew)")]
    HandshakeCertsExpired {
        /// For how long has the circuit been expired?
        expired_by: Duration,
    },
    /// Protocol violation at the channel level, other than at the handshake
    /// stage.
    #[error("Channel protocol violation: {0}")]
    ChanProto(String),
    /// Protocol violation at the circuit level
    #[error("Circuit protocol violation: {0}")]
    CircProto(String),
    /// Channel is closed, or became closed while we were trying to do some
    /// operation.
    #[error("Channel closed")]
    ChannelClosed(#[from] ChannelClosed),
    /// Circuit is closed, or became closed while we were trying to so some
    /// operation.
    #[error("Circuit closed")]
    CircuitClosed,
    /// Can't allocate any more circuit or stream IDs on a channel.
    #[error("Too many entries in map: can't allocate ID")]
    IdRangeFull,
    /// Received a stream request with a stream ID that is already in use for another stream.
    #[error("Stream ID {0} is already in use")]
    IdUnavailable(StreamId),
    /// Received a cell with a stream ID of zero.
    #[error("Received a cell with a stream ID of zero")]
    StreamIdZero,
    /// Couldn't extend a circuit because the extending relay or the
    /// target relay refused our request.
    #[error("Circuit extension refused: {0}")]
    CircRefused(&'static str),
    /// Tried to make or use a stream to an invalid destination address.
    #[error("Invalid stream target address")]
    BadStreamAddress,
    /// Received an End cell from the other end of a stream.
    #[error("Received an END cell with reason {0}")]
    EndReceived(EndReason),
    /// Stream was already closed when we tried to use it.
    #[error("Stream not connected")]
    NotConnected,
    /// Stream protocol violation
    #[error("Stream protocol violation: {0}")]
    StreamProto(String),

    /// Channel does not match target
    #[error("Peer identity mismatch: {0}")]
    ChanMismatch(String),
    /// There was a programming error somewhere in our code, or the calling code.
    #[error("Programming error")]
    Bug(#[from] tor_error::Bug),
    /// Remote DNS lookup failed.
    #[error("Remote resolve failed")]
    ResolveError(#[source] ResolveError),
    /// We tried to do something with a that we couldn't, because of an identity key type
    /// that the relay doesn't have.
    #[error("Relay has no {0} identity")]
    MissingId(RelayIdType),
}

/// Error which indicates that the channel was closed.
#[derive(Error, Debug, Clone)]
#[error("Channel closed")]
pub struct ChannelClosed;

impl HasKind for ChannelClosed {
    fn kind(&self) -> ErrorKind {
        ErrorKind::CircuitCollapse
    }
}

/// Details about an error received while resolving a domain
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum ResolveError {
    /// A transient error which can be retried
    #[error("Received retriable transient error")]
    Transient,
    /// A non transient error, which shouldn't be retried
    #[error("Received non-retriable error")]
    Nontransient,
    /// Could not parse the response properly
    #[error("Received unrecognized result")]
    Unrecognized,
}

impl Error {
    /// Create an error from a tor_cell error that has occurred while trying to
    /// encode or create something of type `object`
    pub(crate) fn from_cell_enc(err: tor_cell::Error, object: &'static str) -> Error {
        Error::CellEncodeErr { object, err }
    }

    /// Create an error from a tor_cell error that has occurred while trying to
    /// decode something of type `object`
    pub(crate) fn from_cell_dec(err: tor_cell::Error, object: &'static str) -> Error {
        match err {
            tor_cell::Error::ChanProto(msg) => Error::ChanProto(msg),
            _ => Error::CellDecodeErr { err, object },
        }
    }

    /// Create an error for a tor_bytes error that occurred while parsing
    /// something of type `object`.
    pub(crate) fn from_bytes_err(err: tor_bytes::Error, object: &'static str) -> Error {
        Error::BytesErr { err, object }
    }

    /// Create an error for a tor_bytes error that occurred while encoding
    /// something of type `object`.
    pub(crate) fn from_bytes_enc(err: tor_bytes::EncodeError, object: &'static str) -> Error {
        Error::EncodeErr { err, object }
    }
}

impl From<Error> for std::io::Error {
    fn from(err: Error) -> std::io::Error {
        use std::io::ErrorKind;
        use Error::*;
        let kind = match err {
            ChanIoErr(e) | HandshakeIoErr(e) => match Arc::try_unwrap(e) {
                Ok(e) => return e,
                Err(arc) => return std::io::Error::new(arc.kind(), arc),
            },

            InvalidKDFOutputLength | NoSuchHop | BadStreamAddress => ErrorKind::InvalidInput,

            NotConnected => ErrorKind::NotConnected,

            EndReceived(end_reason) => end_reason.into(),

            CircuitClosed => ErrorKind::ConnectionReset,

            BytesErr { .. }
            | BadCellAuth
            | BadCircHandshakeAuth
            | HandshakeProto(_)
            | HandshakeCertErr(_)
            | ChanProto(_)
            | HandshakeCertsExpired { .. }
            | ChannelClosed(_)
            | CircProto(_)
            | CellDecodeErr { .. }
            | CellEncodeErr { .. }
            | EncodeErr { .. }
            | ChanMismatch(_)
            | StreamProto(_)
            | MissingId(_)
            | IdUnavailable(_)
            | StreamIdZero => ErrorKind::InvalidData,

            Bug(ref e) if e.kind() == tor_error::ErrorKind::BadApiUsage => ErrorKind::InvalidData,

            IdRangeFull | CircRefused(_) | ResolveError(_) | Bug(_) => ErrorKind::Other,
        };
        std::io::Error::new(kind, err)
    }
}

impl HasKind for Error {
    fn kind(&self) -> ErrorKind {
        use tor_bytes::Error as BytesError;
        use Error as E;
        use ErrorKind as EK;
        match self {
            E::BytesErr {
                err: BytesError::Bug(e),
                ..
            } => e.kind(),
            E::BytesErr { .. } => EK::TorProtocolViolation,
            E::ChanIoErr(_) => EK::LocalNetworkError,
            E::HandshakeIoErr(_) => EK::TorAccessFailed,
            E::HandshakeCertErr(_) => EK::TorProtocolViolation,
            E::CellEncodeErr { err, .. } => err.kind(),
            E::CellDecodeErr { err, .. } => err.kind(),
            E::EncodeErr { .. } => EK::BadApiUsage,
            E::InvalidKDFOutputLength => EK::Internal,
            E::NoSuchHop => EK::BadApiUsage,
            E::BadCellAuth => EK::TorProtocolViolation,
            E::BadCircHandshakeAuth => EK::TorProtocolViolation,
            E::HandshakeProto(_) => EK::TorAccessFailed,
            E::HandshakeCertsExpired { .. } => EK::ClockSkew,
            E::ChanProto(_) => EK::TorProtocolViolation,
            E::CircProto(_) => EK::TorProtocolViolation,
            E::ChannelClosed(e) => e.kind(),
            E::CircuitClosed => EK::CircuitCollapse,
            E::IdRangeFull => EK::BadApiUsage,
            E::CircRefused(_) => EK::CircuitRefused,
            E::BadStreamAddress => EK::BadApiUsage,
            E::EndReceived(reason) => reason.kind(),
            E::NotConnected => EK::BadApiUsage,
            E::StreamProto(_) => EK::TorProtocolViolation,
            E::ChanMismatch(_) => EK::RelayIdMismatch,
            E::ResolveError(ResolveError::Nontransient) => EK::RemoteHostNotFound,
            E::ResolveError(ResolveError::Transient) => EK::RemoteHostResolutionFailed,
            E::ResolveError(ResolveError::Unrecognized) => EK::RemoteHostResolutionFailed,
            E::MissingId(_) => EK::BadApiUsage,
            E::IdUnavailable(_) => EK::BadApiUsage,
            E::StreamIdZero => EK::BadApiUsage,
            E::Bug(e) => e.kind(),
        }
    }
}

/// Internal type: Error return value from reactor's run_once
/// function: indicates an error or a shutdown.
#[derive(Debug)]
pub(crate) enum ReactorError {
    /// The reactor should shut down with an abnormal exit condition.
    Err(Error),
    /// The reactor should shut down without an error, since all is well.
    Shutdown,
}
impl From<Error> for ReactorError {
    fn from(e: Error) -> ReactorError {
        ReactorError::Err(e)
    }
}
impl From<ChannelClosed> for ReactorError {
    fn from(e: ChannelClosed) -> ReactorError {
        ReactorError::Err(e.into())
    }
}
#[cfg(test)]
impl ReactorError {
    /// Tests only: assert that this is an Error, and return it.
    pub(crate) fn unwrap_err(self) -> Error {
        match self {
            ReactorError::Shutdown => panic!(),
            ReactorError::Err(e) => e,
        }
    }
}