Skip to main content

tor_proto/util/
err.rs

1//! Define an error type for the tor-proto crate.
2use safelog::Sensitive;
3use std::{sync::Arc, time::Duration};
4use thiserror::Error;
5use tor_cell::relaycell::{StreamId, msg::EndReason};
6use tor_error::{Bug, ErrorKind, HasKind};
7use tor_linkspec::RelayIdType;
8
9use crate::HopNum;
10use crate::client::circuit::PathEntry;
11
12/// An error type for the tor-proto crate.
13///
14/// This type should probably be split into several.  There's more
15/// than one kind of error that can occur while doing something with
16/// the Tor protocol.
17#[derive(Error, Debug, Clone)]
18#[non_exhaustive]
19pub enum Error {
20    /// An error that occurred in the tor_bytes crate while decoding an
21    /// object.
22    #[error("Unable to parse {object}")]
23    BytesErr {
24        /// What we were trying to parse.
25        object: &'static str,
26        /// The error that occurred while parsing it.
27        #[source]
28        err: tor_bytes::Error,
29    },
30    /// An error that occurred from the io system when using a
31    /// channel.
32    #[error("IO error on channel with peer")]
33    ChanIoErr(#[source] Arc<std::io::Error>),
34    /// An error from the io system that occurred when trying to connect a channel.
35    #[error("IO error while handshaking with peer")]
36    HandshakeIoErr(#[source] Arc<std::io::Error>),
37    /// An error occurred while trying to create or encode a cell.
38    #[error("Unable to generate or encode {object}")]
39    CellEncodeErr {
40        /// The object we were trying to create or encode.
41        object: &'static str,
42        /// The error that occurred.
43        #[source]
44        err: tor_cell::Error,
45    },
46    /// An error occurred while trying to decode or parse a cell.
47    #[error("Error while parsing {object}")]
48    CellDecodeErr {
49        /// The object we were trying to decode.
50        object: &'static str,
51        /// The error that occurred.
52        #[source]
53        err: tor_cell::Error,
54    },
55    /// An error occurred while trying to create or encode some non-cell
56    /// message.
57    ///
58    /// This is likely the result of a bug: either in this crate, or the code
59    /// that provided the input.
60    #[error("Problem while encoding {object}")]
61    EncodeErr {
62        /// What we were trying to create or encode.
63        object: &'static str,
64        /// The error that occurred.
65        #[source]
66        err: tor_bytes::EncodeError,
67    },
68    /// An error occurred while trying to decode a link specifier.
69    #[error("Error while parsing {object}")]
70    #[cfg(feature = "relay")]
71    LinkspecDecodeErr {
72        /// The object we were trying to decode.
73        object: &'static str,
74        /// The error that occurred.
75        #[source]
76        err: tor_linkspec::decode::ChanTargetDecodeError,
77    },
78    /// We found a problem with one of the certificates in the channel
79    /// handshake.
80    #[error("Problem with certificate on handshake")]
81    HandshakeCertErr(#[source] tor_cert::CertError),
82    /// We tried to produce too much output for a key derivation function.
83    #[error("Tried to extract too many bytes from a KDF")]
84    InvalidKDFOutputLength,
85    /// We tried to encrypt a message to a hop that wasn't there.
86    #[error("Tried to encrypt a cell for a nonexistent hop")]
87    NoSuchHop,
88    /// The authentication information on this cell was completely wrong,
89    /// or the cell was corrupted.
90    #[error("Bad relay cell authentication")]
91    BadCellAuth,
92    /// A circuit-extension handshake failed due to a mismatched authentication
93    /// value.
94    #[error("Circuit-extension handshake authentication failed")]
95    BadCircHandshakeAuth,
96    /// Handshake protocol violation.
97    #[error("Handshake protocol violation: {0}")]
98    HandshakeProto(String),
99    /// Handshake broken, maybe due to clock skew.
100    ///
101    /// (If the problem can't be due to clock skew, we return HandshakeProto
102    /// instead.)
103    #[error("Handshake failed due to expired certificates (possible clock skew)")]
104    HandshakeCertsExpired {
105        /// For how long has the circuit been expired?
106        expired_by: Duration,
107    },
108    /// Protocol violation at the channel level, other than at the handshake
109    /// stage.
110    #[error("Channel protocol violation: {0}")]
111    ChanProto(String),
112    /// Protocol violation at the circuit level
113    #[error("Circuit protocol violation: {0}")]
114    CircProto(String),
115    /// Channel is closed, or became closed while we were trying to do some
116    /// operation.
117    #[error("Channel closed")]
118    ChannelClosed(#[from] ChannelClosed),
119    /// Circuit is closed, or became closed while we were trying to so some
120    /// operation.
121    #[error("Circuit closed")]
122    CircuitClosed,
123    /// Can't allocate any more circuit or stream IDs on a channel.
124    #[error("Too many entries in map: can't allocate ID")]
125    IdRangeFull,
126    /// Received a stream request with a stream ID that is already in use for another stream.
127    #[error("Stream ID {0} is already in use")]
128    IdUnavailable(StreamId),
129    /// Received a cell with a stream ID of zero.
130    #[error("Received a cell with a stream ID of zero")]
131    StreamIdZero,
132    /// Received a cell on a closed or non-existent stream.
133    #[error(
134        "Received a cell from {} on a closed or non-existent stream {}. \
135         Either they are violating the protocol, or we are expiring streams too aggressively",
136        src,
137        streamid
138    )]
139    UnknownStream {
140        /// The hop the cell originated from.
141        src: Sensitive<PathEntry>,
142        /// The stream ID of the cell.
143        streamid: StreamId,
144    },
145    /// Couldn't extend a circuit because the extending relay or the
146    /// target relay refused our request.
147    #[error("Circuit extension refused: {0}")]
148    CircRefused(&'static str),
149    /// Tried to make or use a stream to an invalid destination address.
150    #[error("Invalid stream target address")]
151    BadStreamAddress,
152    /// Received an End cell from the other end of a stream.
153    #[error("Received an END cell with reason {0}")]
154    EndReceived(EndReason),
155    /// Stream was already closed when we tried to use it.
156    #[error("Stream not connected")]
157    NotConnected,
158    /// Stream protocol violation
159    #[error("Stream protocol violation: {0}")]
160    StreamProto(String),
161
162    /// Excessive data received from a circuit hop.
163    #[error("Received too many inbound cells")]
164    ExcessInboundCells,
165    /// Tried to send too many cells to a circuit hop.
166    #[error("Tried to send too many outbound cells")]
167    ExcessOutboundCells,
168
169    /// Received unexpected or excessive inbound circuit
170    #[error("Received unexpected or excessive circuit padding from {}", _1.display())]
171    ExcessPadding(#[source] ExcessPadding, HopNum),
172
173    /// Channel does not match target
174    #[error("Peer identity mismatch: {0}")]
175    ChanMismatch(String),
176    /// There was a programming error somewhere in our code, or the calling code.
177    #[error("Programming error")]
178    Bug(#[from] tor_error::Bug),
179    /// Remote DNS lookup failed.
180    #[error("Remote resolve failed")]
181    ResolveError(#[source] ResolveError),
182    /// We tried to do something with a that we couldn't, because of an identity key type
183    /// that the relay doesn't have.
184    #[error("Relay has no {0} identity")]
185    MissingId(RelayIdType),
186    /// Memory quota error
187    #[error("memory quota error")]
188    Memquota(#[from] tor_memquota::Error),
189    /// An unsupported authentication type value. This is raised during the relay <-> relay channel
190    /// handshake if the authentication presented from the other side is unsupported.
191    #[cfg(feature = "relay")]
192    #[error("Authentication type {0} is unsupported")]
193    UnsupportedAuth(u16),
194}
195
196/// Error which indicates that the channel was closed.
197#[derive(Error, Debug, Clone)]
198#[error("Channel closed")]
199pub struct ChannelClosed;
200
201impl HasKind for ChannelClosed {
202    fn kind(&self) -> ErrorKind {
203        ErrorKind::CircuitCollapse
204    }
205}
206
207/// Details about an error received while resolving a domain
208#[derive(Error, Debug, Clone)]
209#[non_exhaustive]
210pub enum ResolveError {
211    /// A transient error which can be retried
212    #[error("Received retriable transient error")]
213    Transient,
214    /// A non transient error, which shouldn't be retried
215    #[error("Received non-retriable error")]
216    Nontransient,
217    /// Could not parse the response properly
218    #[error("Received unrecognized result")]
219    Unrecognized,
220}
221
222impl Error {
223    /// Create an error from a tor_cell error that has occurred while trying to
224    /// encode or create something of type `object`
225    pub(crate) fn from_cell_enc(err: tor_cell::Error, object: &'static str) -> Error {
226        Error::CellEncodeErr { object, err }
227    }
228
229    /// Create an error for a tor_bytes error that occurred while parsing
230    /// something of type `object`.
231    pub(crate) fn from_bytes_err(err: tor_bytes::Error, object: &'static str) -> Error {
232        Error::BytesErr { err, object }
233    }
234
235    /// Create an error for a tor_bytes error that occurred while encoding
236    /// something of type `object`.
237    pub(crate) fn from_bytes_enc(err: tor_bytes::EncodeError, object: &'static str) -> Error {
238        Error::EncodeErr { err, object }
239    }
240}
241
242impl From<std::io::Error> for Error {
243    fn from(err: std::io::Error) -> Self {
244        Self::ChanIoErr(Arc::new(err))
245    }
246}
247
248impl From<Error> for std::io::Error {
249    fn from(err: Error) -> std::io::Error {
250        use Error::*;
251        use std::io::ErrorKind;
252        let kind = match err {
253            ChanIoErr(e) | HandshakeIoErr(e) => match Arc::try_unwrap(e) {
254                Ok(e) => return e,
255                Err(arc) => return std::io::Error::new(arc.kind(), arc),
256            },
257
258            InvalidKDFOutputLength | NoSuchHop | BadStreamAddress => ErrorKind::InvalidInput,
259
260            NotConnected => ErrorKind::NotConnected,
261
262            EndReceived(end_reason) => end_reason.into(),
263
264            CircuitClosed => ErrorKind::ConnectionReset,
265
266            Memquota { .. } => ErrorKind::OutOfMemory,
267
268            BytesErr { .. }
269            | BadCellAuth
270            | BadCircHandshakeAuth
271            | HandshakeProto(_)
272            | HandshakeCertErr(_)
273            | ChanProto(_)
274            | HandshakeCertsExpired { .. }
275            | ChannelClosed(_)
276            | CircProto(_)
277            | CellDecodeErr { .. }
278            | CellEncodeErr { .. }
279            | EncodeErr { .. }
280            | ChanMismatch(_)
281            | StreamProto(_)
282            | MissingId(_)
283            | IdUnavailable(_)
284            | StreamIdZero
285            | UnknownStream { .. }
286            | ExcessInboundCells
287            | ExcessOutboundCells
288            | ExcessPadding(_, _) => ErrorKind::InvalidData,
289
290            #[cfg(feature = "relay")]
291            LinkspecDecodeErr { .. } => ErrorKind::InvalidData,
292            #[cfg(feature = "relay")]
293            UnsupportedAuth(_) => ErrorKind::Unsupported,
294
295            Bug(ref e) if e.kind() == tor_error::ErrorKind::BadApiUsage => ErrorKind::InvalidData,
296
297            IdRangeFull | CircRefused(_) | ResolveError(_) | Bug(_) => ErrorKind::Other,
298        };
299        std::io::Error::new(kind, err)
300    }
301}
302
303impl HasKind for Error {
304    fn kind(&self) -> ErrorKind {
305        use Error as E;
306        use ErrorKind as EK;
307        use tor_bytes::Error as BytesError;
308        match self {
309            E::BytesErr {
310                err: BytesError::Bug(e),
311                ..
312            } => e.kind(),
313            E::BytesErr { .. } => EK::TorProtocolViolation,
314            E::ChanIoErr(_) => EK::LocalNetworkError,
315            E::HandshakeIoErr(_) => EK::TorAccessFailed,
316            E::HandshakeCertErr(_) => EK::TorProtocolViolation,
317            E::CellEncodeErr { err, .. } => err.kind(),
318            E::CellDecodeErr { err, .. } => err.kind(),
319            #[cfg(feature = "relay")]
320            E::LinkspecDecodeErr { .. } => EK::TorProtocolViolation,
321            E::EncodeErr { .. } => EK::BadApiUsage,
322            E::InvalidKDFOutputLength => EK::Internal,
323            E::NoSuchHop => EK::BadApiUsage,
324            E::BadCellAuth => EK::TorProtocolViolation,
325            E::BadCircHandshakeAuth => EK::TorProtocolViolation,
326            E::HandshakeProto(_) => EK::TorAccessFailed,
327            E::HandshakeCertsExpired { .. } => EK::ClockSkew,
328            E::ChanProto(_) => EK::TorProtocolViolation,
329            E::CircProto(_) => EK::TorProtocolViolation,
330            E::ChannelClosed(e) => e.kind(),
331            E::CircuitClosed => EK::CircuitCollapse,
332            E::IdRangeFull => EK::BadApiUsage,
333            E::CircRefused(_) => EK::CircuitRefused,
334            E::BadStreamAddress => EK::BadApiUsage,
335            E::EndReceived(reason) => reason.kind(),
336            E::NotConnected => EK::BadApiUsage,
337            E::StreamProto(_) => EK::TorProtocolViolation,
338            E::ChanMismatch(_) => EK::RelayIdMismatch,
339            E::ResolveError(ResolveError::Nontransient) => EK::RemoteHostNotFound,
340            E::ResolveError(ResolveError::Transient) => EK::RemoteHostResolutionFailed,
341            E::ResolveError(ResolveError::Unrecognized) => EK::RemoteHostResolutionFailed,
342            E::MissingId(_) => EK::BadApiUsage,
343            E::IdUnavailable(_) => EK::BadApiUsage,
344            E::StreamIdZero => EK::BadApiUsage,
345            E::UnknownStream { .. } => EK::TorProtocolViolation,
346            E::ExcessInboundCells => EK::TorProtocolViolation,
347            E::ExcessOutboundCells => EK::Internal,
348            E::ExcessPadding(_, _) => EK::TorProtocolViolation,
349            E::Memquota(err) => err.kind(),
350            E::Bug(e) => e.kind(),
351            #[cfg(feature = "relay")]
352            E::UnsupportedAuth(_) => EK::RemoteProtocolViolation,
353        }
354    }
355}
356
357/// Internal type: Error return value from reactor's run_once
358/// function: indicates an error or a shutdown.
359#[derive(Debug)]
360pub(crate) enum ReactorError {
361    /// The reactor should shut down with an abnormal exit condition.
362    Err(Error),
363    /// The reactor should shut down without an error, since all is well.
364    Shutdown,
365}
366
367impl From<Error> for ReactorError {
368    fn from(e: Error) -> ReactorError {
369        ReactorError::Err(e)
370    }
371}
372
373impl From<ChannelClosed> for ReactorError {
374    fn from(e: ChannelClosed) -> ReactorError {
375        ReactorError::Err(e.into())
376    }
377}
378
379impl From<Bug> for ReactorError {
380    fn from(e: Bug) -> ReactorError {
381        ReactorError::Err(e.into())
382    }
383}
384
385#[cfg(test)]
386impl ReactorError {
387    /// Tests only: assert that this is an Error, and return it.
388    pub(crate) fn unwrap_err(self) -> Error {
389        match self {
390            ReactorError::Shutdown => panic!(),
391            ReactorError::Err(e) => e,
392        }
393    }
394}
395
396/// An error type for client-side conflux handshakes.
397#[derive(Debug)]
398#[cfg(feature = "conflux")]
399#[allow(unused)] // TODO(conflux): remove
400pub(crate) enum ConfluxHandshakeError {
401    /// Timeout while waiting for CONFLUX_LINKED response.
402    Timeout,
403    /// An error that occurred while sending the CONFLUX_LINK message.
404    Link(Error),
405    /// The channel was closed.
406    ChannelClosed,
407}
408
409/// An error returned when we receive excessive or unexpected padding.
410#[derive(Debug, Clone, Error)]
411#[non_exhaustive]
412pub enum ExcessPadding {
413    /// We received circuit padding when we hadn't negotiated a padding framework.
414    #[error("Padding received when not negotiated with given hop")]
415    NoPaddingNegotiated,
416    /// We received more padding than our negotiated framework permits.
417    #[error("Received padding in excess of negotiated framework's limit")]
418    PaddingExceedsLimit,
419}