diff --git a/crates/tor-circmgr/src/build.rs b/crates/tor-circmgr/src/build.rs index 12ec9150b8a7fb5a667b25ad77fc067e4e6512ee..1768cdbd1bd480d34ba248a94bf292d98d7d59d6 100644 --- a/crates/tor-circmgr/src/build.rs +++ b/crates/tor-circmgr/src/build.rs @@ -80,7 +80,10 @@ async fn create_common<RT: Runtime, CT: ChanTarget>( peer: OwnedChanTarget::from_chan_target(target), cause, })?; - let (pending_circ, reactor) = chan.new_circ().await?; + let (pending_circ, reactor) = chan.new_circ().await.map_err(|error| Error::Protocol { + error, + peer: None, // we don't blame the peer, because new_circ() does no networking. + })?; rt.spawn(async { let _ = reactor.run().await; @@ -99,7 +102,12 @@ impl Buildable for ClientCirc { params: &CircParameters, ) -> Result<Self> { let circ = create_common(chanmgr, rt, ct).await?; - Ok(circ.create_firsthop_fast(params).await?) + circ.create_firsthop_fast(params) + .await + .map_err(|error| Error::Protocol { + peer: Some(ct.clone()), + error, + }) } async fn create<RT: Runtime>( chanmgr: &ChanMgr<RT>, @@ -108,7 +116,12 @@ impl Buildable for ClientCirc { params: &CircParameters, ) -> Result<Self> { let circ = create_common(chanmgr, rt, ct).await?; - Ok(circ.create_firsthop_ntor(ct, params.clone()).await?) + circ.create_firsthop_ntor(ct, params.clone()) + .await + .map_err(|error| Error::Protocol { + peer: Some(OwnedChanTarget::from_chan_target(ct)), + error, + }) } async fn extend<RT: Runtime>( &self, @@ -116,8 +129,12 @@ impl Buildable for ClientCirc { ct: &OwnedCircTarget, params: &CircParameters, ) -> Result<()> { - self.extend_ntor(ct, params).await?; - Ok(()) + self.extend_ntor(ct, params) + .await + .map_err(|error| Error::Protocol { + error, + peer: None, // we don't know who caused the error. + }) } } diff --git a/crates/tor-circmgr/src/err.rs b/crates/tor-circmgr/src/err.rs index 41498e20e65084a31cfb6819bc885712eccede84..93f554a50c61a3022a6e3b0bf68645c44c1e976c 100644 --- a/crates/tor-circmgr/src/err.rs +++ b/crates/tor-circmgr/src/err.rs @@ -76,8 +76,16 @@ pub enum Error { }, /// Protocol issue while building a circuit. - #[error("Problem building a circuit: {0}")] - Protocol(#[from] tor_proto::Error), + #[error("Problem building a circuit with {peer:?}")] + Protocol { + /// The peer that created the protocol error. + /// + /// This is set to None if we can't blame a single party. + peer: Option<OwnedChanTarget>, + /// The underlying error. + #[source] + error: tor_proto::Error, + }, /// We have an expired consensus #[error("Consensus is expired")] @@ -143,7 +151,7 @@ impl HasKind for Error { .map(|e| e.kind()) .unwrap_or(EK::Internal), E::CircCanceled => EK::TransientFailure, - E::Protocol(e) => e.kind(), + E::Protocol { error, .. } => error.kind(), E::State(e) => e.kind(), E::GuardMgr(e) => e.kind(), E::Guard(e) => e.kind(), @@ -179,11 +187,23 @@ impl Error { E::Guard(_) => 40, E::RequestFailed(_) => 40, E::Channel { .. } => 40, - E::Protocol(_) => 45, + E::Protocol { .. } => 45, E::ExpiredConsensus => 50, E::Spawn { .. } => 90, E::State(_) => 90, E::Bug(_) => 100, } } + + /// Return a list of the peers to "blame" for this error, if there are any. + pub fn peers(&self) -> Vec<&OwnedChanTarget> { + match self { + Error::RequestFailed(errors) => errors.sources().flat_map(|e| e.peers()).collect(), + Error::Channel { peer, .. } => vec![peer], + Error::Protocol { + peer: Some(peer), .. + } => vec![peer], + _ => vec![], + } + } } diff --git a/doc/semver_status.md b/doc/semver_status.md index 59bc6ecd590950f95740a6015907ca10b529c404..3e75b1c5d3b52382744a51d9fc0b53f12655d37a 100644 --- a/doc/semver_status.md +++ b/doc/semver_status.md @@ -54,6 +54,8 @@ tor-circmgr: api-break: The fallbacks case of DirInfo now wants a slice of references to fallbacks. + api-break: Some error types have changed to include peer info. + tor-dirmgr: new-api: DirMgrConfig object now has accessors. DirMgrCfg: totally changed, builder abolished.