Commit 34b576a8 authored by Nick Mathewson's avatar Nick Mathewson 🦀
Browse files

Integrate GuardUsability and GuardMonitor into CircuitBuilder.

(When we're building a path with a guard, we need to tell the guard
manager whether the path succeeded, and we need to wait to hear
whether the guard is usable.)
parent 33ba697b
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -24,6 +24,11 @@ pub enum Error {
    #[error("Circuit took too long to build")]
    CircTimeout,

    /// We started building a circuit on a guard, but later decided not
    /// to use that guard.
    #[error("Discarded circuit because of speculative guard selection")]
    GuardNotUsable,

    /// Tried to take a circuit for a purpose it doesn't support.
    #[error("Circuit usage not supported: {0}")]
    UsageNotSupported(String),
+54 −4
Original line number Diff line number Diff line
@@ -3,8 +3,9 @@
use crate::mgr::{self};
use crate::path::OwnedPath;
use crate::usage::{SupportedCircUsage, TargetCircUsage};
use crate::{DirInfo, Result};
use crate::{DirInfo, Error, Result};
use async_trait::async_trait;
use futures::future::OptionFuture;
use rand::{rngs::StdRng, SeedableRng};
use std::convert::TryInto;
use std::sync::Arc;
@@ -31,6 +32,13 @@ pub(crate) struct Plan {
    path: OwnedPath,
    /// The protocol parameters to use when constructing the circuit.
    params: CircParameters,
    /// If this path is using a guard, we'll use this object to report
    /// whether the circuit succeeded or failed.
    guard_status: Option<tor_guardmgr::GuardMonitor>,
    /// If this path is using a guard, we'll use this object to learn
    /// whether we're allowed ot use the circuit or whether we have to
    /// wait a while.
    guard_usable: Option<tor_guardmgr::GuardUsable>,
}

#[async_trait]
@@ -45,12 +53,15 @@ impl<R: Runtime> crate::mgr::AbstractCircBuilder for crate::build::CircuitBuilde
        dir: DirInfo<'_>,
    ) -> Result<(Plan, SupportedCircUsage)> {
        let mut rng = rand::thread_rng();
        let (path, final_spec) = usage.build_path(&mut rng, dir, self.path_config())?;
        let (path, final_spec, guard_status, guard_usable) =
            usage.build_path(&mut rng, dir, self.path_config())?;

        let plan = Plan {
            final_spec: final_spec.clone(),
            path: (&path).try_into()?,
            params: dir.circ_params(),
            guard_status,
            guard_usable,
        };

        Ok((plan, final_spec))
@@ -61,12 +72,51 @@ impl<R: Runtime> crate::mgr::AbstractCircBuilder for crate::build::CircuitBuilde
            final_spec,
            path,
            params,
            guard_status,
            guard_usable,
        } = plan;
        let rng = StdRng::from_rng(rand::thread_rng()).expect("couldn't construct temporary rng");

        let circuit = self.build_owned(path, &params, rng).await?;
        let guard_usable: OptionFuture<_> = guard_usable.into();

        // TODO: We may want to lower the logic for handling
        // guard_status and guard_usable into build.rs, so that they
        // can be handled correctly on user-selected paths as well.
        //
        // This will probably require a different API for circuit
        // construction.
        match self.build_owned(path, &params, rng).await {
            Ok(circuit) => {
                if let Some(mon) = guard_status {
                    // Report success to the guard manager, so it knows that
                    // this guard is reachable.
                    // TODO: We may someday want to report two-hop circuits
                    // as successful. But not today.
                    mon.succeeded();
                }
                // We have to wait for the guard manager to tell us whether
                // this guard is actually _usable_ or not.  Possibly,
                // it is a speculative guard that we're only trying out
                // in case some preferable guard won't meet our needs.
                match guard_usable.await {
                    Some(Ok(true)) | None => (),
                    Some(Ok(false)) => return Err(Error::GuardNotUsable),
                    Some(Err(_)) => {
                        return Err(Error::Internal("Guard usability status cancelled".into()))
                    }
                }
                Ok((final_spec, circuit))
            }
            Err(e) => {
                if let Some(mon) = guard_status {
                    // Report failure to the guard manager, so it knows
                    // not to use this guard in the future.
                    mon.failed();
                }
                Err(e)
            }
        }
    }

    fn launch_parallelism(&self, spec: &TargetCircUsage) -> usize {
        match spec {
+14 −7
Original line number Diff line number Diff line
@@ -4,11 +4,11 @@ use rand::Rng;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;

use crate::path::{dirpath::DirPathBuilder, exitpath::ExitPathBuilder, TorPath};
use tor_guardmgr::{GuardMonitor, GuardUsable};
use tor_netdir::Relay;
use tor_netdoc::types::policy::PortPolicy;

use crate::path::{dirpath::DirPathBuilder, exitpath::ExitPathBuilder, TorPath};

use crate::{Error, Result};

/// An exit policy, as supported by the last hop of a circuit.
@@ -173,11 +173,16 @@ impl TargetCircUsage {
        rng: &mut R,
        netdir: crate::DirInfo<'a>,
        config: &crate::PathConfig,
    ) -> Result<(TorPath<'a>, SupportedCircUsage)> {
    ) -> Result<(
        TorPath<'a>,
        SupportedCircUsage,
        Option<GuardMonitor>,
        Option<GuardUsable>,
    )> {
        match self {
            TargetCircUsage::Dir => {
                let path = DirPathBuilder::new().pick_path(rng, netdir)?;
                Ok((path, SupportedCircUsage::Dir))
                Ok((path, SupportedCircUsage::Dir, None, None))
            }
            TargetCircUsage::Exit {
                ports: p,
@@ -194,6 +199,8 @@ impl TargetCircUsage {
                        policy,
                        isolation_group: Some(*isolation_group),
                    },
                    None,
                    None,
                ))
            }
            TargetCircUsage::TimeoutTesting => {
@@ -207,7 +214,7 @@ impl TargetCircUsage {
                    _ => SupportedCircUsage::NoUsage,
                };

                Ok((path, usage))
                Ok((path, usage, None, None))
            }
        }
    }
@@ -468,7 +475,7 @@ mod test {
        // Only doing basic tests for now.  We'll test the path
        // building code a lot more closely in the tests for TorPath
        // and friends.
        let (p_dir, u_dir) = TargetCircUsage::Dir
        let (p_dir, u_dir, _, _) = TargetCircUsage::Dir
            .build_path(&mut rng, di, &config)
            .unwrap();
        assert!(matches!(u_dir, SupportedCircUsage::Dir));
@@ -479,7 +486,7 @@ mod test {
            ports: vec![TargetPort::ipv4(995)],
            isolation_group,
        };
        let (p_exit, u_exit) = exit_usage.build_path(&mut rng, di, &config).unwrap();
        let (p_exit, u_exit, _, _) = exit_usage.build_path(&mut rng, di, &config).unwrap();
        assert!(matches!(
            u_exit,
            SupportedCircUsage::Exit {