aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriela Moldovan <gabi@torproject.org>2024-03-13 11:11:10 +0000
committerGabriela Moldovan <gabi@torproject.org>2024-03-14 12:27:14 +0000
commit21b5a828529d46292f6432947f62ff830b033241 (patch)
tree17fc9ae4bc658708ce5113851b75b31e169efcb1
parent6b120ec2117b8b0dfb5f0daa1746ec532cf3ad3a (diff)
downloadarti-21b5a828529d46292f6432947f62ff830b033241.tar.gz
arti-21b5a828529d46292f6432947f62ff830b033241.zip
tor-circmgr: Add AnonymousPathBuilder trait.
The `pick_path()` default implementation is an almost verbatim copy of `ExitPathBuilder::pick_path()`. The only difference is that instead of using values from `self`, the `AnonymousPathBuilder::pick_path()` uses the values returned by the different `AnonymousPathBuilder` functions (i.e., `AnonymousPathBuilder::pick_exit()` instead of `self.pick_exit()`, `AnonymousPathBuilder::compatible_with()` instead of `self.compatible_with`, etc.).
-rw-r--r--crates/tor-circmgr/src/path.rs181
1 files changed, 177 insertions, 4 deletions
diff --git a/crates/tor-circmgr/src/path.rs b/crates/tor-circmgr/src/path.rs
index 2d91c4e26..8bc51e109 100644
--- a/crates/tor-circmgr/src/path.rs
+++ b/crates/tor-circmgr/src/path.rs
@@ -6,15 +6,22 @@
pub mod dirpath;
pub mod exitpath;
-use tor_error::bad_api_usage;
+use std::time::SystemTime;
+
+use rand::Rng;
+
+use tor_error::{bad_api_usage, internal};
#[cfg(feature = "geoip")]
use tor_geoip::{CountryCode, HasCountryCode};
use tor_guardmgr::fallback::FallbackDir;
-use tor_linkspec::{HasAddrs, HasRelayIds, OwnedChanTarget, OwnedCircTarget};
-use tor_netdir::Relay;
+use tor_guardmgr::{GuardMgr, GuardMonitor, GuardUsable};
+use tor_linkspec::{HasAddrs, HasRelayIds, OwnedChanTarget, OwnedCircTarget, RelayIdSet};
+use tor_netdir::{NetDir, Relay};
+use tor_relay_selection::{RelayExclusion, RelaySelectionConfig, RelaySelector, RelayUsage};
+use tor_rtcompat::Runtime;
use crate::usage::ExitPolicy;
-use crate::Result;
+use crate::{DirInfo, Error, PathConfig, Result};
/// A list of Tor relays through the network.
pub struct TorPath<'a> {
@@ -237,6 +244,172 @@ impl OwnedPath {
}
}
+/// A path builder that builds multi-hop, anonymous paths.
+pub trait AnonymousPathBuilder<'a> {
+ /// Return the relay to use as exit node.
+ fn chosen_exit(&self) -> Option<&Relay<'_>>;
+
+ /// Return the "target" that every chosen relay must be able to share a circuit with with.
+ fn compatible_with(&self) -> Option<&OwnedChanTarget>;
+
+ /// Return a short description of the path we're trying to build,
+ /// for error reporting purposes.
+ fn path_kind(&self) -> &'static str;
+
+ /// Find a suitable exit node from either the chosen exit or from the network directory.
+ ///
+ /// Return the exit, along with the usage for a middle node corresponding
+ /// to this exit.
+ fn pick_exit<'s, R: Rng>(
+ &'s self,
+ rng: &mut R,
+ netdir: &'a NetDir,
+ guard_exclusion: RelayExclusion<'a>,
+ rs_cfg: &RelaySelectionConfig<'_>,
+ ) -> Result<(Relay<'a>, RelayUsage)>;
+
+ /// Try to create and return a path corresponding to the requirements of
+ /// this builder.
+ fn pick_path<'s, R: Rng, RT: Runtime>(
+ &'s self,
+ rng: &mut R,
+ netdir: DirInfo<'a>,
+ guards: Option<&GuardMgr<RT>>,
+ config: &PathConfig,
+ _now: SystemTime,
+ ) -> Result<(TorPath<'a>, Option<GuardMonitor>, Option<GuardUsable>)> {
+ let netdir = match netdir {
+ DirInfo::Directory(d) => d,
+ _ => {
+ return Err(bad_api_usage!(
+ "Tried to build a multihop path without a network directory"
+ )
+ .into())
+ }
+ };
+ let rs_cfg = config.relay_selection_config();
+
+ let chosen_exit = self.chosen_exit();
+ let path_is_fully_random = chosen_exit.is_none();
+
+ // TODO-SPEC: Because of limitations in guard selection, we have to
+ // pick the guard before the exit, which is not what our spec says.
+ let (guard, mon, usable) = match guards {
+ Some(guardmgr) => {
+ // TODO: Extract this section into its own function, and see
+ // what it can share with tor_relay_selection.
+ let mut b = tor_guardmgr::GuardUsageBuilder::default();
+ b.kind(tor_guardmgr::GuardUsageKind::Data);
+ if let Some(exit_relay) = chosen_exit {
+ // TODO(nickm): Our way of building a family here is
+ // somewhat questionable. We're only adding the ed25519
+ // identities of the exit relay and its family to the
+ // RelayId set. That's fine for now, since we will only use
+ // relays at this point if they have a known Ed25519
+ // identity. But if in the future the ed25519 identity
+ // becomes optional, this will need to change.
+ let mut family = RelayIdSet::new();
+ family.insert(*exit_relay.id());
+ // TODO(nickm): See "limitations" note on `known_family_members`.
+ family.extend(netdir.known_family_members(exit_relay).map(|r| *r.id()));
+ b.restrictions()
+ .push(tor_guardmgr::GuardRestriction::AvoidAllIds(family));
+ }
+ if let Some(avoid_target) = self.compatible_with() {
+ let mut family = RelayIdSet::new();
+ family.extend(avoid_target.identities().map(|id| id.to_owned()));
+ if let Some(avoid_relay) = netdir.by_ids(avoid_target) {
+ family.extend(netdir.known_family_members(&avoid_relay).map(|r| *r.id()));
+ }
+ b.restrictions()
+ .push(tor_guardmgr::GuardRestriction::AvoidAllIds(family));
+ }
+ let guard_usage = b.build().expect("Failed while building guard usage!");
+ let (guard, mut mon, usable) = guardmgr.select_guard(guard_usage)?;
+ let guard = if let Some(ct) = guard.as_circ_target() {
+ // This is a bridge; we will not look for it in the network directory.
+ MaybeOwnedRelay::from(ct.clone())
+ } else {
+ // Look this up in the network directory: we expect to find a relay.
+ guard
+ .get_relay(netdir)
+ .ok_or_else(|| {
+ internal!(
+ "Somehow the guardmgr gave us an unlisted guard {:?}!",
+ guard
+ )
+ })?
+ .into()
+ };
+ if !path_is_fully_random {
+ // We were given a specific exit relay to use, and
+ // the choice of exit relay might be forced by
+ // something outside of our control.
+ //
+ // Therefore, we must not blame the guard for any failure
+ // to complete the circuit.
+ mon.ignore_indeterminate_status();
+ }
+ (guard, Some(mon), Some(usable))
+ }
+ None => {
+ let rs_cfg = config.relay_selection_config();
+ let exclusion = match chosen_exit {
+ Some(r) => {
+ RelayExclusion::exclude_relays_in_same_family(&rs_cfg, vec![r.clone()])
+ }
+ None => RelayExclusion::no_relays_excluded(),
+ };
+ let selector = RelaySelector::new(RelayUsage::new_guard(), exclusion);
+ let (relay, info) = selector.select_relay(rng, netdir);
+ let relay = relay.ok_or_else(|| Error::NoRelay {
+ path_kind: self.path_kind(),
+ role: "entry",
+ problem: info.to_string(),
+ })?;
+
+ (MaybeOwnedRelay::from(relay), None, None)
+ }
+ };
+
+ let guard_exclusion = match &guard {
+ MaybeOwnedRelay::Relay(r) => RelayExclusion::exclude_relays_in_same_family(
+ &config.relay_selection_config(),
+ vec![r.clone()],
+ ),
+ MaybeOwnedRelay::Owned(ct) => RelayExclusion::exclude_channel_target_family(
+ &config.relay_selection_config(),
+ ct.as_ref(),
+ netdir,
+ ),
+ };
+
+ let (exit, middle_usage) = self.pick_exit(rng, netdir, guard_exclusion.clone(), &rs_cfg)?;
+
+ let mut family_exclusion =
+ RelayExclusion::exclude_relays_in_same_family(&rs_cfg, vec![exit.clone()]);
+ family_exclusion.extend(&guard_exclusion);
+
+ let selector = RelaySelector::new(middle_usage, family_exclusion);
+ let (middle, info) = selector.select_relay(rng, netdir);
+ let middle = middle.ok_or_else(|| Error::NoRelay {
+ path_kind: self.path_kind(),
+ role: "middle relay",
+ problem: info.to_string(),
+ })?;
+
+ Ok((
+ TorPath::new_multihop_from_maybe_owned(vec![
+ guard,
+ MaybeOwnedRelay::from(middle),
+ MaybeOwnedRelay::from(exit),
+ ]),
+ mon,
+ usable,
+ ))
+ }
+}
+
/// For testing: make sure that `path` is the same when it is an owned
/// path.
#[cfg(test)]