//! Define an error type for the tor-proto crate. use safelog::Sensitive; use std::{sync::Arc, time::Duration}; use thiserror::Error; use tor_cell::relaycell::{StreamId, msg::EndReason}; use tor_error::{Bug, ErrorKind, HasKind}; use tor_linkspec::RelayIdType; use crate::HopNum; use crate::client::circuit::PathEntry; /// 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), /// An error from the io system that occurred when trying to connect a channel. #[error("IO error while handshaking with peer")] HandshakeIoErr(#[source] Arc), /// 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, }, /// An error occurred while trying to decode a link specifier. #[error("Error while parsing {object}")] #[cfg(feature = "relay")] LinkspecDecodeErr { /// The object we were trying to decode. object: &'static str, /// The error that occurred. #[source] err: tor_linkspec::decode::ChanTargetDecodeError, }, /// 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, /// Received a cell on a closed or non-existent stream. #[error( "Received a cell from {} on a closed or non-existent stream {}. \ Either they are violating the protocol, or we are expiring streams too aggressively", src, streamid )] UnknownStream { /// The hop the cell originated from. src: Sensitive, /// The stream ID of the cell. streamid: StreamId, }, /// 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), /// Excessive data received from a circuit hop. #[error("Received too many inbound cells")] ExcessInboundCells, /// Tried to send too many cells to a circuit hop. #[error("Tried to send too many outbound cells")] ExcessOutboundCells, /// Received unexpected or excessive inbound circuit #[error("Received unexpected or excessive circuit padding from {}", _1.display())] ExcessPadding(#[source] ExcessPadding, HopNum), /// 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), /// Memory quota error #[error("memory quota error")] Memquota(#[from] tor_memquota::Error), /// An unsupported authentication type value. This is raised during the relay <-> relay channel /// handshake if the authentication presented from the other side is unsupported. #[cfg(feature = "relay")] #[error("Authentication type {0} is unsupported")] UnsupportedAuth(u16), } /// 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 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 for Error { fn from(err: std::io::Error) -> Self { Self::ChanIoErr(Arc::new(err)) } } impl From for std::io::Error { fn from(err: Error) -> std::io::Error { use Error::*; use std::io::ErrorKind; 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, Memquota { .. } => ErrorKind::OutOfMemory, BytesErr { .. } | BadCellAuth | BadCircHandshakeAuth | HandshakeProto(_) | HandshakeCertErr(_) | ChanProto(_) | HandshakeCertsExpired { .. } | ChannelClosed(_) | CircProto(_) | CellDecodeErr { .. } | CellEncodeErr { .. } | EncodeErr { .. } | ChanMismatch(_) | StreamProto(_) | MissingId(_) | IdUnavailable(_) | StreamIdZero | UnknownStream { .. } | ExcessInboundCells | ExcessOutboundCells | ExcessPadding(_, _) => ErrorKind::InvalidData, #[cfg(feature = "relay")] LinkspecDecodeErr { .. } => ErrorKind::InvalidData, #[cfg(feature = "relay")] UnsupportedAuth(_) => ErrorKind::Unsupported, 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 Error as E; use ErrorKind as EK; use tor_bytes::Error as BytesError; 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(), #[cfg(feature = "relay")] E::LinkspecDecodeErr { .. } => EK::TorProtocolViolation, 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::UnknownStream { .. } => EK::TorProtocolViolation, E::ExcessInboundCells => EK::TorProtocolViolation, E::ExcessOutboundCells => EK::Internal, E::ExcessPadding(_, _) => EK::TorProtocolViolation, E::Memquota(err) => err.kind(), E::Bug(e) => e.kind(), #[cfg(feature = "relay")] E::UnsupportedAuth(_) => EK::RemoteProtocolViolation, } } } /// 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 for ReactorError { fn from(e: Error) -> ReactorError { ReactorError::Err(e) } } impl From for ReactorError { fn from(e: ChannelClosed) -> ReactorError { ReactorError::Err(e.into()) } } impl From for ReactorError { fn from(e: Bug) -> 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, } } } /// An error type for client-side conflux handshakes. #[derive(Debug)] #[cfg(feature = "conflux")] #[allow(unused)] // TODO(conflux): remove pub(crate) enum ConfluxHandshakeError { /// Timeout while waiting for CONFLUX_LINKED response. Timeout, /// An error that occurred while sending the CONFLUX_LINK message. Link(Error), /// The channel was closed. ChannelClosed, } /// An error returned when we receive excessive or unexpected padding. #[derive(Debug, Clone, Error)] #[non_exhaustive] pub enum ExcessPadding { /// We received circuit padding when we hadn't negotiated a padding framework. #[error("Padding received when not negotiated with given hop")] NoPaddingNegotiated, /// We received more padding than our negotiated framework permits. #[error("Received padding in excess of negotiated framework's limit")] PaddingExceedsLimit, }