1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
//! Code to construct paths to a directory for non-anonymous downloads
use super::TorPath;
use crate::{DirInfo, Error, Result};
use tor_guardmgr::{GuardMgr, GuardMonitor, GuardUsable};
use tor_netdir::{Relay, WeightRole};
use tor_rtcompat::Runtime;
use rand::{seq::SliceRandom, Rng};
/// A PathBuilder that can connect to a directory.
#[non_exhaustive]
pub struct DirPathBuilder {}
impl Default for DirPathBuilder {
fn default() -> Self {
Self::new()
}
}
impl DirPathBuilder {
/// Create a new DirPathBuilder.
pub fn new() -> Self {
DirPathBuilder {}
}
/// Try to create and return a path corresponding to the requirements of
/// this builder.
pub fn pick_path<'a, R: Rng, RT: Runtime>(
&self,
rng: &mut R,
netdir: DirInfo<'a>,
guards: Option<&GuardMgr<RT>>,
) -> Result<(TorPath<'a>, Option<GuardMonitor>, Option<GuardUsable>)> {
match (netdir, guards) {
(DirInfo::Fallbacks(f), _) => {
let relay = f.choose(rng);
if let Some(r) = relay {
return Ok((TorPath::new_fallback_one_hop(r), None, None));
}
}
(DirInfo::Directory(netdir), None) => {
let relay = netdir.pick_relay(rng, WeightRole::BeginDir, Relay::is_dir_cache);
if let Some(r) = relay {
return Ok((TorPath::new_one_hop(r), None, None));
}
}
(DirInfo::Directory(netdir), Some(guardmgr)) => {
guardmgr.update_network(netdir); // possibly unnecessary.
let guard_usage = tor_guardmgr::GuardUsageBuilder::default()
.kind(tor_guardmgr::GuardUsageKind::OneHopDirectory)
.build()
.expect("Unable to build directory guard usage");
let (guard, mon, usable) = guardmgr.select_guard(guard_usage, Some(netdir))?;
if let Some(r) = guard.get_relay(netdir) {
return Ok((TorPath::new_one_hop(r), Some(mon), Some(usable)));
}
}
}
Err(Error::NoRelays(
"No relays found for use as directory cache".into(),
))
}
}
#[cfg(test)]
mod test {
#![allow(clippy::unwrap_used)]
use super::*;
use crate::path::assert_same_path_when_owned;
use crate::test::OptDummyGuardMgr;
use tor_netdir::fallback::FallbackDir;
use tor_netdir::testnet;
#[test]
fn dirpath_relay() {
let netdir = testnet::construct_netdir()
.unwrap()
.unwrap_if_sufficient()
.unwrap();
let mut rng = rand::thread_rng();
let dirinfo = (&netdir).into();
let guards: OptDummyGuardMgr<'_> = None;
for _ in 0..1000 {
let p = DirPathBuilder::default().pick_path(&mut rng, dirinfo, guards);
let (p, _, _) = p.unwrap();
assert!(p.exit_relay().is_none());
assert_eq!(p.len(), 1);
assert_same_path_when_owned(&p);
if let crate::path::TorPathInner::OneHop(r) = p.inner {
assert!(r.is_dir_cache());
} else {
panic!("Generated the wrong kind of path.");
}
}
}
#[test]
fn dirpath_fallback() {
let fb = vec![
FallbackDir::builder()
.rsa_identity([0x01; 20].into())
.ed_identity([0x01; 32].into())
.orport("127.0.0.1:9000".parse().unwrap())
.build()
.unwrap(),
FallbackDir::builder()
.rsa_identity([0x03; 20].into())
.ed_identity([0x03; 32].into())
.orport("127.0.0.1:9003".parse().unwrap())
.build()
.unwrap(),
];
let dirinfo = (&fb[..]).into();
let mut rng = rand::thread_rng();
let guards: OptDummyGuardMgr<'_> = None;
for _ in 0..10 {
let p = DirPathBuilder::default().pick_path(&mut rng, dirinfo, guards);
let (p, _, _) = p.unwrap();
assert!(p.exit_relay().is_none());
assert_eq!(p.len(), 1);
assert_same_path_when_owned(&p);
if let crate::path::TorPathInner::FallbackOneHop(f) = p.inner {
assert!(std::ptr::eq(f, &fb[0]) || std::ptr::eq(f, &fb[1]));
} else {
panic!("Generated the wrong kind of path.");
}
}
}
#[test]
fn dirpath_no_fallbacks() {
let fb = vec![];
let dirinfo = DirInfo::Fallbacks(&fb[..]);
let mut rng = rand::thread_rng();
let guards: OptDummyGuardMgr<'_> = None;
let err = DirPathBuilder::default().pick_path(&mut rng, dirinfo, guards);
assert!(matches!(err, Err(Error::NoRelays(_))));
}
}
|