pub mod dirpath;
pub mod exitpath;
use tor_error::bad_api_usage;
use tor_guardmgr::fallback::FallbackDir;
use tor_linkspec::{HasAddrs, HasRelayIds, OwnedChanTarget, OwnedCircTarget};
use tor_netdir::Relay;
use crate::usage::ExitPolicy;
use crate::Result;
pub struct TorPath<'a> {
inner: TorPathInner<'a>,
}
enum TorPathInner<'a> {
OneHop(Relay<'a>), FallbackOneHop(&'a FallbackDir),
OwnedOneHop(OwnedChanTarget),
Path(Vec<MaybeOwnedRelay<'a>>),
}
#[derive(Clone)]
enum MaybeOwnedRelay<'a> {
Relay(Relay<'a>),
Owned(Box<OwnedCircTarget>),
}
impl<'a> MaybeOwnedRelay<'a> {
fn to_owned(&self) -> OwnedCircTarget {
match self {
MaybeOwnedRelay::Relay(r) => OwnedCircTarget::from_circ_target(r),
MaybeOwnedRelay::Owned(o) => o.as_ref().clone(),
}
}
}
impl<'a> From<OwnedCircTarget> for MaybeOwnedRelay<'a> {
fn from(ct: OwnedCircTarget) -> Self {
MaybeOwnedRelay::Owned(Box::new(ct))
}
}
impl<'a> From<Relay<'a>> for MaybeOwnedRelay<'a> {
fn from(r: Relay<'a>) -> Self {
MaybeOwnedRelay::Relay(r)
}
}
impl<'a> HasAddrs for MaybeOwnedRelay<'a> {
fn addrs(&self) -> &[std::net::SocketAddr] {
match self {
MaybeOwnedRelay::Relay(r) => r.addrs(),
MaybeOwnedRelay::Owned(r) => r.addrs(),
}
}
}
impl<'a> HasRelayIds for MaybeOwnedRelay<'a> {
fn identity(
&self,
key_type: tor_linkspec::RelayIdType,
) -> Option<tor_linkspec::RelayIdRef<'_>> {
match self {
MaybeOwnedRelay::Relay(r) => r.identity(key_type),
MaybeOwnedRelay::Owned(r) => r.identity(key_type),
}
}
}
impl<'a> TorPath<'a> {
pub fn new_one_hop(relay: Relay<'a>) -> Self {
Self {
inner: TorPathInner::OneHop(relay),
}
}
pub fn new_fallback_one_hop(fallback_dir: &'a FallbackDir) -> Self {
Self {
inner: TorPathInner::FallbackOneHop(fallback_dir),
}
}
pub fn new_one_hop_owned<T: tor_linkspec::ChanTarget>(target: &T) -> Self {
Self {
inner: TorPathInner::OwnedOneHop(OwnedChanTarget::from_chan_target(target)),
}
}
pub fn new_multihop<H>(relays: impl IntoIterator<Item = Relay<'a>>) -> Self {
Self {
inner: TorPathInner::Path(relays.into_iter().map(MaybeOwnedRelay::from).collect()),
}
}
fn new_multihop_from_maybe_owned(relays: Vec<MaybeOwnedRelay<'a>>) -> Self {
Self {
inner: TorPathInner::Path(relays),
}
}
fn exit_relay(&self) -> Option<&MaybeOwnedRelay<'a>> {
match &self.inner {
TorPathInner::Path(relays) if !relays.is_empty() => Some(&relays[relays.len() - 1]),
_ => None,
}
}
pub(crate) fn exit_policy(&self) -> Option<ExitPolicy> {
self.exit_relay().and_then(|r| match r {
MaybeOwnedRelay::Relay(r) => Some(ExitPolicy::from_relay(r)),
MaybeOwnedRelay::Owned(_) => None,
})
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
use TorPathInner::*;
match &self.inner {
OneHop(_) => 1,
FallbackOneHop(_) => 1,
OwnedOneHop(_) => 1,
Path(p) => p.len(),
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum OwnedPath {
ChannelOnly(OwnedChanTarget),
Normal(Vec<OwnedCircTarget>),
}
impl<'a> TryFrom<&TorPath<'a>> for OwnedPath {
type Error = crate::Error;
fn try_from(p: &TorPath<'a>) -> Result<OwnedPath> {
use TorPathInner::*;
Ok(match &p.inner {
FallbackOneHop(h) => OwnedPath::ChannelOnly(OwnedChanTarget::from_chan_target(*h)),
OneHop(h) => OwnedPath::Normal(vec![OwnedCircTarget::from_circ_target(h)]),
OwnedOneHop(owned) => OwnedPath::ChannelOnly(owned.clone()),
Path(p) if !p.is_empty() => {
OwnedPath::Normal(p.iter().map(MaybeOwnedRelay::to_owned).collect())
}
Path(_) => {
return Err(bad_api_usage!("Path with no entries!").into());
}
})
}
}
impl OwnedPath {
#[allow(clippy::len_without_is_empty)]
pub(crate) fn len(&self) -> usize {
match self {
OwnedPath::ChannelOnly(_) => 1,
OwnedPath::Normal(p) => p.len(),
}
}
}
#[cfg(test)]
fn assert_same_path_when_owned(path: &TorPath<'_>) {
#![allow(clippy::unwrap_used)]
let owned: OwnedPath = path.try_into().unwrap();
match (&owned, &path.inner) {
(OwnedPath::ChannelOnly(c), TorPathInner::FallbackOneHop(f)) => {
assert!(c.same_relay_ids(*f));
}
(OwnedPath::Normal(p), TorPathInner::OneHop(h)) => {
assert_eq!(p.len(), 1);
assert!(p[0].same_relay_ids(h));
}
(OwnedPath::Normal(p1), TorPathInner::Path(p2)) => {
assert_eq!(p1.len(), p2.len());
for (n1, n2) in p1.iter().zip(p2.iter()) {
assert!(n1.same_relay_ids(n2));
}
}
(_, _) => {
panic!("Mismatched path types.");
}
}
}