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
//! Define an error type for the tor-proto crate.
use std::{sync::Arc, time::Duration};
use thiserror::Error;
use tor_cell::relaycell::msg::EndReason;
use tor_error::{ErrorKind, HasKind};

/// 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("parsing error: {0}")]
    BytesErr(#[from] tor_bytes::Error),
    /// An error that occurred from the io system when using a
    /// channel.
    #[error("io error on channel: {0}")]
    ChanIoErr(#[source] Arc<std::io::Error>),
    /// An error from the io system that occurred when trying to connect a channel.
    #[error("io error in handshake: {0}")]
    HandshakeIoErr(#[source] Arc<std::io::Error>),
    /// An error occurred in the cell-handling layer.
    #[error("cell encoding error: {0}")]
    CellErr(#[source] tor_cell::Error),
    /// We tried to produce too much output for some function.
    #[error("couldn't produce that much output")]
    InvalidOutputLength,
    /// We tried to encrypt a message to a hop that wasn't there.
    #[error("tried to encrypt to 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.
    #[error("handshake failed")]
    BadCircHandshake,
    /// 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.
    #[error("channel closed")]
    ChannelClosed,
    /// Circuit is closed.
    #[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,
    /// Couldn't extend a circuit because the extending relay or the
    /// target relay refused our request.
    #[error("circuit extension handshake error: {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("channel mismatch: {0}")]
    ChanMismatch(String),
    /// There was a programming error somewhere in our code, or the calling code.
    #[error("Programming error: {0}")]
    Bug(#[from] tor_error::Bug),
    /// Remote DNS lookup failed.
    #[error("remote resolve failed: {0}")]
    ResolveError(ResolveError),
}

/// 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 not retriable error")]
    Nontransient,
    /// Could not parse the response properly
    #[error("received unrecognized result")]
    Unrecognized,
}

impl From<tor_cell::Error> for Error {
    fn from(err: tor_cell::Error) -> Error {
        match err {
            tor_cell::Error::ChanProto(msg) => Error::ChanProto(msg),
            _ => Error::CellErr(err),
        }
    }
}

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),
            },

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

            NotConnected => ErrorKind::NotConnected,

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

            ChannelClosed | CircuitClosed => ErrorKind::ConnectionReset,

            BytesErr(_)
            | BadCellAuth
            | BadCircHandshake
            | HandshakeProto(_)
            | ChanProto(_)
            | HandshakeCertsExpired { .. }
            | CircProto(_)
            | CellErr(_)
            | ChanMismatch(_)
            | StreamProto(_) => 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(BytesError::Bug(e)) => e.kind(),
            E::BytesErr(_) => EK::TorProtocolViolation,
            E::ChanIoErr(_) => EK::LocalNetworkError,
            E::HandshakeIoErr(_) => EK::TorAccessFailed,
            E::CellErr(e) => e.kind(),
            E::InvalidOutputLength => EK::Internal,
            E::NoSuchHop => EK::BadApiUsage,
            E::BadCellAuth => EK::TorProtocolViolation,
            E::BadCircHandshake => EK::TorProtocolViolation,
            E::HandshakeProto(_) => EK::TorAccessFailed,
            E::HandshakeCertsExpired { .. } => EK::ClockSkew,
            E::ChanProto(_) => EK::TorProtocolViolation,
            E::CircProto(_) => EK::TorProtocolViolation,
            E::ChannelClosed | 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::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)
    }
}
#[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,
        }
    }
}