aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgabi-250 <gabi@torproject.org>2024-03-14 13:00:28 +0000
committergabi-250 <gabi@torproject.org>2024-03-14 13:00:28 +0000
commite3a8e918228a62a64e7cc0bf1ff717eb875f59f2 (patch)
tree344fea1e2d519572af0d0c26a52d4ef0946b4c7e
parent6b120ec2117b8b0dfb5f0daa1746ec532cf3ad3a (diff)
parentfc7c647f05a3f0ddde556bb065d357d6fc842211 (diff)
downloadarti-1333-ensure-that-transport-configuration-has-tests-in-arti-cfg.tar.gz
arti-1333-ensure-that-transport-configuration-has-tests-in-arti-cfg.zip
Merge branch 'exitpath-onion-svc' into 'main'1333-ensure-that-transport-configuration-has-tests-in-arti-cfg
tor-circmgr: Add HsPathBuilder for building onion service paths See merge request tpo/core/arti!2038
-rw-r--r--.gitlab-ci.yml3
-rw-r--r--crates/tor-circmgr/src/path.rs181
-rw-r--r--crates/tor-circmgr/src/path/exitpath.rs237
-rw-r--r--crates/tor-circmgr/src/path/hspath.rs124
-rw-r--r--crates/tor-circmgr/src/usage.rs10
-rw-r--r--tests/shadow/README.md2
6 files changed, 349 insertions, 208 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 19f375594..1d47a83b0 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -95,12 +95,13 @@ rust-latest:
image: rust:bookworm
script:
- rustup show
- - cargo build --locked --verbose --target x86_64-unknown-linux-gnu
+ - cargo check --locked --verbose --target x86_64-unknown-linux-gnu
- cargo test --verbose --target x86_64-unknown-linux-gnu
- rustup component add clippy
- rustup show
- cargo clippy --all-features --all-targets -- --cfg ci_arti_stable -D warnings
- cargo build --verbose --release -p arti-bench --target x86_64-unknown-linux-gnu
+ - cargo build --locked --verbose --target x86_64-unknown-linux-gnu -p arti
- ./maint/preserve target/x86_64-unknown-linux-gnu/debug/arti target/x86_64-unknown-linux-gnu/release/arti-bench
after_script:
- cargo clean
diff --git a/crates/tor-circmgr/src/path.rs b/crates/tor-circmgr/src/path.rs
index 2d91c4e26..36f27a3fc 100644
--- a/crates/tor-circmgr/src/path.rs
+++ b/crates/tor-circmgr/src/path.rs
@@ -5,16 +5,25 @@
pub mod dirpath;
pub mod exitpath;
+#[cfg(feature = "hs-common")]
+pub mod hspath;
-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 +246,170 @@ impl OwnedPath {
}
}
+/// A path builder that builds multi-hop, anonymous paths.
+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, 'a, B: AnonymousPathBuilder<'a>, R: Rng, RT: Runtime>(
+ builder: &B,
+ 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 = builder.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) = builder.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: builder.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) = builder.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: builder.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)]
diff --git a/crates/tor-circmgr/src/path/exitpath.rs b/crates/tor-circmgr/src/path/exitpath.rs
index d54d82896..7b4b2829d 100644
--- a/crates/tor-circmgr/src/path/exitpath.rs
+++ b/crates/tor-circmgr/src/path/exitpath.rs
@@ -1,19 +1,20 @@
//! Code for building paths to an exit relay.
-use super::{MaybeOwnedRelay, TorPath};
-use crate::{DirInfo, Error, PathConfig, Result, TargetPort};
-use rand::Rng;
use std::time::SystemTime;
-use tor_error::{bad_api_usage, internal};
-#[cfg(feature = "geoip")]
-use tor_geoip::CountryCode;
+
+use rand::Rng;
+
+use super::{AnonymousPathBuilder, TorPath};
+use crate::path::pick_path;
+use crate::{DirInfo, Error, PathConfig, Result, TargetPort};
+
use tor_guardmgr::{GuardMgr, GuardMonitor, GuardUsable};
-use tor_linkspec::{HasRelayIds, OwnedChanTarget, RelayIdSet};
+use tor_linkspec::OwnedChanTarget;
use tor_netdir::{NetDir, Relay};
-use tor_relay_selection::{
- RelayExclusion, RelayRestriction, RelaySelectionConfig, RelaySelector, RelayUsage,
-};
+use tor_relay_selection::{RelayExclusion, RelaySelectionConfig, RelaySelector, RelayUsage};
use tor_rtcompat::Runtime;
+#[cfg(feature = "geoip")]
+use {tor_geoip::CountryCode, tor_relay_selection::RelayRestriction};
/// Internal representation of PathBuilder.
enum ExitPathBuilderInner<'a> {
@@ -41,11 +42,6 @@ enum ExitPathBuilderInner<'a> {
strict: bool,
},
- /// Request a path to any relay, even those that cannot exit.
- // TODO: #785 may make this non-conditional.
- #[cfg(feature = "hs-common")]
- AnyRelayForOnionService,
-
/// Request a path that uses a given relay as exit node.
ChosenExit(Relay<'a>),
}
@@ -119,24 +115,17 @@ impl<'a> ExitPathBuilder<'a> {
}
}
- /// Create a new builder that will try to build a three-hop non-exit path
- /// for use with the onion services protocols
- /// that is compatible with being extended to an optional given relay.
- ///
- /// (The provided relay is _not_ included in the built path: we only ensure
- /// that the path we build does not have any features that would stop us
- /// extending it to that relay as a fourth hop.)
- ///
- /// TODO: This doesn't seem to belong in a type called ExitPathBuilder.
- /// Perhaps we should rename ExitPathBuilder, split it into multiple types,
- /// or move this method.
- #[cfg(feature = "hs-common")]
- pub(crate) fn for_onion_service(compatible_with: Option<OwnedChanTarget>) -> Self {
- Self {
- inner: ExitPathBuilderInner::AnyRelayForOnionService,
- compatible_with,
- require_stability: true,
- }
+ /// Try to create and return a path corresponding to the requirements of
+ /// this builder.
+ pub fn pick_path<R: Rng, RT: Runtime>(
+ &self,
+ rng: &mut R,
+ netdir: DirInfo<'a>,
+ guards: Option<&GuardMgr<RT>>,
+ config: &PathConfig,
+ now: SystemTime,
+ ) -> Result<(TorPath<'a>, Option<GuardMonitor>, Option<GuardUsable>)> {
+ pick_path(self, rng, netdir, guards, config, now)
}
/// Create a new builder that will try to get an exit relay, but which
@@ -155,13 +144,23 @@ impl<'a> ExitPathBuilder<'a> {
self.require_stability = require_stability;
self
}
+}
- /// 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<R: Rng>(
- &self,
+impl<'a> AnonymousPathBuilder<'a> for ExitPathBuilder<'a> {
+ fn chosen_exit(&self) -> Option<&Relay<'_>> {
+ if let ExitPathBuilderInner::ChosenExit(e) = &self.inner {
+ Some(e)
+ } else {
+ None
+ }
+ }
+
+ fn compatible_with(&self) -> Option<&OwnedChanTarget> {
+ self.compatible_with.as_ref()
+ }
+
+ fn pick_exit<'s, R: Rng>(
+ &'s self,
rng: &mut R,
netdir: &'a NetDir,
guard_exclusion: RelayExclusion<'a>,
@@ -187,14 +186,6 @@ impl<'a> ExitPathBuilder<'a> {
selector
}
- #[cfg(feature = "hs-common")]
- ExitPathBuilderInner::AnyRelayForOnionService => {
- // TODO: This usage is a bit convoluted, and some onion-service-
- // related circuits don't need this much stability.
- let usage = RelayUsage::middle_relay(Some(&RelayUsage::new_intro_point()));
- RelaySelector::new(usage, guard_exclusion)
- }
-
ExitPathBuilderInner::WantsPorts(wantports) => RelaySelector::new(
RelayUsage::exit_to_all_ports(rs_cfg, wantports.clone()),
guard_exclusion,
@@ -217,153 +208,6 @@ impl<'a> ExitPathBuilder<'a> {
Ok((relay, RelayUsage::middle_relay(Some(selector.usage()))))
}
- /// Try to create and return a path corresponding to the requirements of
- /// this builder.
- pub fn pick_path<R: Rng, RT: Runtime>(
- &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 = if let ExitPathBuilderInner::ChosenExit(e) = &self.inner {
- Some(e)
- } else {
- None
- };
- 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,
- ))
- }
-
- /// Return a short description of the path we're trying to build,
- /// for error reporting purposes.
fn path_kind(&self) -> &'static str {
use ExitPathBuilderInner::*;
match &self.inner {
@@ -371,7 +215,6 @@ impl<'a> ExitPathBuilder<'a> {
#[cfg(feature = "geoip")]
ExitInCountry { .. } => "country-specific exit circuit",
AnyExit { .. } => "testing circuit",
- AnyRelayForOnionService => "onion-service circuit",
ChosenExit(_) => "circuit to a specific exit",
}
}
@@ -393,7 +236,9 @@ mod test {
#![allow(clippy::needless_pass_by_value)]
//! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
use super::*;
- use crate::path::{assert_same_path_when_owned, MaybeOwnedRelay, OwnedPath, TorPathInner};
+ use crate::path::{
+ assert_same_path_when_owned, MaybeOwnedRelay, OwnedPath, TorPath, TorPathInner,
+ };
use crate::test::OptDummyGuardMgr;
use std::collections::HashSet;
use tor_basic_utils::test_rng::testing_rng;
diff --git a/crates/tor-circmgr/src/path/hspath.rs b/crates/tor-circmgr/src/path/hspath.rs
new file mode 100644
index 000000000..4fab2eb70
--- /dev/null
+++ b/crates/tor-circmgr/src/path/hspath.rs
@@ -0,0 +1,124 @@
+//! Code for building paths for HS circuits.
+
+use rand::Rng;
+use tor_linkspec::OwnedChanTarget;
+use tor_netdir::{NetDir, Relay};
+use tor_relay_selection::{RelayExclusion, RelaySelectionConfig, RelaySelector, RelayUsage};
+
+use crate::{Error, Result};
+
+use super::AnonymousPathBuilder;
+
+use {
+ crate::path::TorPath,
+ crate::{DirInfo, PathConfig},
+ std::time::SystemTime,
+ tor_guardmgr::{GuardMgr, GuardMonitor, GuardUsable},
+ tor_rtcompat::Runtime,
+};
+
+/// A path builder for hidden service circuits.
+///
+/// This builder is used for creating hidden service stub circuits,
+/// which are three-hop circuits that have not yet been extended to a target.
+///
+/// Stub circuits eventually become introduction, rendezvous, and HsDir circuits.
+/// For all circuit types except client rendezvous, the stubs must first be
+/// extended by an extra hop:
+///
+/// ```text
+/// Client hsdir: STUB+ -> HsDir
+/// Client intro: STUB+ -> Ipt
+/// Client rend: STUB
+/// Service hsdir: STUB -> HsDir
+/// Service intro: STUB -> Ipt
+/// Service rend: STUB+ -> Rpt
+/// ```
+///
+/// While we don't currently distinguish between regular stub circuits (STUB),
+/// and extended stub circuits (STUB+), the two will be handled differently
+/// once we add support for vanguards.
+pub struct HsPathBuilder {
+ /// If present, a "target" that every chosen relay must be able to share a circuit with with.
+ compatible_with: Option<OwnedChanTarget>,
+}
+
+impl HsPathBuilder {
+ /// Create a new builder that will try to build a three-hop non-exit path
+ /// for use with the onion services protocols
+ /// that is compatible with being extended to an optional given relay.
+ ///
+ /// (The provided relay is _not_ included in the built path: we only ensure
+ /// that the path we build does not have any features that would stop us
+ /// extending it to that relay as a fourth hop.)
+ pub(crate) fn new(compatible_with: Option<OwnedChanTarget>) -> Self {
+ Self { compatible_with }
+ }
+
+ /// Try to create and return a path for a hidden service circuit stub.
+ #[cfg(not(feature = "vanguards"))]
+ pub fn pick_path<'a, R: Rng, RT: Runtime>(
+ &self,
+ rng: &mut R,
+ netdir: DirInfo<'a>,
+ guards: Option<&GuardMgr<RT>>,
+ config: &PathConfig,
+ now: SystemTime,
+ ) -> Result<(TorPath<'a>, Option<GuardMonitor>, Option<GuardUsable>)> {
+ use super::pick_path;
+
+ pick_path(self, rng, netdir, guards, config, now)
+ }
+
+ /// Try to create and return a path for a hidden service circuit stub.
+ #[cfg(feature = "vanguards")]
+ pub fn pick_path<'a, R: Rng, RT: Runtime>(
+ &self,
+ _rng: &mut R,
+ _netdir: DirInfo<'a>,
+ _guards: Option<&GuardMgr<RT>>,
+ _config: &PathConfig,
+ _now: SystemTime,
+ ) -> Result<(TorPath<'a>, Option<GuardMonitor>, Option<GuardUsable>)> {
+ // TODO HS-VANGUARDS (#1279): this will likely share some logic with
+ // AnonymousPathBuilder::pick_path, so we might want to split
+ // AnonymousPathBuilder::pick_path into multiple smaller functions
+ // that we can use here
+ todo!()
+ }
+}
+
+impl<'a> AnonymousPathBuilder<'a> for HsPathBuilder {
+ fn chosen_exit(&self) -> Option<&Relay<'_>> {
+ None
+ }
+
+ fn compatible_with(&self) -> Option<&OwnedChanTarget> {
+ self.compatible_with.as_ref()
+ }
+
+ fn path_kind(&self) -> &'static str {
+ "onion-service circuit"
+ }
+
+ 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)> {
+ // TODO: This usage is a bit convoluted, and some onion-service-
+ // related circuits don't need this much stability.
+ let usage = RelayUsage::middle_relay(Some(&RelayUsage::new_intro_point()));
+ let selector = RelaySelector::new(usage, guard_exclusion);
+
+ let (relay, info) = selector.select_relay(rng, netdir);
+ let relay = relay.ok_or_else(|| Error::NoRelay {
+ path_kind: self.path_kind(),
+ role: "final hop",
+ problem: info.to_string(),
+ })?;
+ Ok((relay, RelayUsage::middle_relay(Some(selector.usage()))))
+ }
+}
diff --git a/crates/tor-circmgr/src/usage.rs b/crates/tor-circmgr/src/usage.rs
index 7c40f037c..d30402030 100644
--- a/crates/tor-circmgr/src/usage.rs
+++ b/crates/tor-circmgr/src/usage.rs
@@ -8,6 +8,8 @@ use tracing::trace;
#[cfg(not(feature = "geoip"))]
use void::Void;
+#[cfg(feature = "hs-common")]
+use crate::path::hspath::HsPathBuilder;
use crate::path::{dirpath::DirPathBuilder, exitpath::ExitPathBuilder, TorPath};
use tor_chanmgr::ChannelUsage;
#[cfg(feature = "geoip")]
@@ -333,12 +335,8 @@ impl TargetCircUsage {
TargetCircUsage::HsCircBase {
compatible_with_target,
} => {
- let (path, mon, usable) =
- ExitPathBuilder::for_onion_service(compatible_with_target.clone())
- // TODO: We don't actually require stability if this is a
- // HsDir circuit: but at this point, we can't tell.
- .require_stability(true)
- .pick_path(rng, netdir, guards, config, now)?;
+ let (path, mon, usable) = HsPathBuilder::new(compatible_with_target.clone())
+ .pick_path(rng, netdir, guards, config, now)?;
let usage = SupportedCircUsage::HsOnly;
Ok((path, usage, mon, usable))
}
diff --git a/tests/shadow/README.md b/tests/shadow/README.md
index 533d431a9..e7b78c308 100644
--- a/tests/shadow/README.md
+++ b/tests/shadow/README.md
@@ -37,7 +37,7 @@ locations where [`shadow.yaml`](./shadow.yaml) expects to find them.
job `rust-latest` with the invocation:
```shell
- $ cargo build --locked --verbose --target x86_64-unknown-linux-gnu
+ $ cargo build --locked --verbose --target x86_64-unknown-linux-gnu -p arti
```
Once those are installed, you can invoke the [`run.sh`](./run.sh) script from