1use crate::{LowLevelRelayPredicate, RelaySelectionConfig, TargetPort};
4use tor_netdir::{Relay, WeightRole};
5
6#[derive(Clone, Debug)]
8pub struct RelayUsage {
9 inner: RelayUsageInner,
11 need_stable: bool,
16}
17
18#[derive(Clone, Debug)]
22enum RelayUsageInner {
23 AnyExit,
25 ExitToAllPorts(Vec<TargetPort>),
27 ExitToAnyPort {
32 stable_ports: Vec<TargetPort>,
34 unstable_ports: Vec<TargetPort>,
36 },
37 Middle,
39 NewIntroPoint,
41 ContinuingIntroPoint,
44 NewGuard,
46 ContinuingGuard,
49 #[cfg(feature = "vanguards")]
51 Vanguard,
52 DirectoryCache,
54}
55
56impl RelayUsage {
57 pub fn any_exit(_cfg: &RelaySelectionConfig) -> Self {
63 RelayUsage {
69 inner: RelayUsageInner::AnyExit,
70 need_stable: false,
71 }
72 }
73
74 pub fn exit_to_all_ports(cfg: &RelaySelectionConfig, ports: Vec<TargetPort>) -> Self {
76 let need_stable = ports.iter().any(|p| cfg.port_requires_stable_flag(p.port));
77 RelayUsage {
78 inner: RelayUsageInner::ExitToAllPorts(ports),
79 need_stable,
80 }
81 }
82
83 pub fn exit_to_any_port(cfg: &RelaySelectionConfig, ports: Vec<TargetPort>) -> Self {
85 let (stable_ports, unstable_ports): (Vec<_>, Vec<_>) = ports
86 .into_iter()
87 .partition(|p| cfg.port_requires_stable_flag(p.port));
88 let need_stable = unstable_ports.is_empty() && !stable_ports.is_empty();
89 RelayUsage {
90 inner: RelayUsageInner::ExitToAnyPort {
91 stable_ports,
92 unstable_ports,
93 },
94 need_stable,
95 }
96 }
97
98 pub fn middle_relay(known_final_hop_usage: Option<&RelayUsage>) -> Self {
110 let need_stable = known_final_hop_usage.map(|u| u.need_stable).unwrap_or(true);
111 RelayUsage {
112 inner: RelayUsageInner::Middle,
113 need_stable,
114 }
115 }
116
117 pub fn new_intro_point() -> Self {
123 RelayUsage {
124 inner: RelayUsageInner::NewIntroPoint,
125 need_stable: true,
126 }
127 }
128
129 pub fn continuing_intro_point() -> Self {
131 RelayUsage {
132 inner: RelayUsageInner::ContinuingIntroPoint,
133 need_stable: true,
134 }
135 }
136
137 pub fn new_guard() -> Self {
143 RelayUsage {
144 inner: RelayUsageInner::NewGuard,
145 need_stable: true,
146 }
147 }
148
149 pub fn continuing_guard() -> Self {
151 RelayUsage {
152 inner: RelayUsageInner::ContinuingGuard,
153 need_stable: true,
154 }
155 }
156
157 #[cfg(feature = "vanguards")]
159 pub fn vanguard() -> Self {
160 RelayUsage {
161 inner: RelayUsageInner::Vanguard,
162 need_stable: true,
164 }
165 }
166
167 pub fn directory_cache() -> Self {
173 RelayUsage {
174 inner: RelayUsageInner::DirectoryCache,
175 need_stable: false,
176 }
177 }
178
179 pub(crate) fn selection_weight_role(&self) -> WeightRole {
181 use RelayUsageInner::*;
182
183 match &self.inner {
184 AnyExit | ExitToAllPorts(_) | ExitToAnyPort { .. } => WeightRole::Exit,
185 Middle => WeightRole::Middle,
186 NewIntroPoint | ContinuingIntroPoint => WeightRole::HsIntro,
187 NewGuard | ContinuingGuard => WeightRole::Guard,
188 #[cfg(feature = "vanguards")]
189 Vanguard => WeightRole::Middle,
190 DirectoryCache => WeightRole::BeginDir,
191 }
192 }
193
194 pub(crate) fn rejection_description(&self) -> &'static str {
197 use RelayUsageInner::*;
198 match &self.inner {
199 AnyExit => "non-exit",
200 ExitToAllPorts(_) => "not exiting to desired ports",
201 ExitToAnyPort { .. } => "not exiting to any desired port",
202 Middle => "useless for middle relay",
203 NewIntroPoint | ContinuingIntroPoint => "not introduction point",
204 NewGuard | ContinuingGuard => "not guard",
205 #[cfg(feature = "vanguards")]
206 Vanguard => "not usable as vanguard",
207 DirectoryCache => "not directory cache",
208 }
209 }
210}
211
212impl LowLevelRelayPredicate for RelayUsage {
213 fn low_level_predicate_permits_relay(&self, relay_in: &Relay<'_>) -> bool {
214 use RelayUsageInner::*;
215 let relay = relay_in.low_level_details();
216 if !relay.is_flagged_fast() {
217 return false;
218 }
219 if self.need_stable && !relay.is_flagged_stable() {
220 return false;
221 }
222 match &self.inner {
223 AnyExit => relay.policies_allow_some_port(),
224 ExitToAllPorts(ports) => ports.iter().all(|p| p.is_supported_by(&relay)),
225 ExitToAnyPort {
226 stable_ports,
227 unstable_ports,
228 } => {
229 if relay.is_flagged_stable()
230 && stable_ports.iter().any(|p| p.is_supported_by(&relay))
231 {
232 return true;
233 }
234 unstable_ports.iter().any(|p| p.is_supported_by(&relay))
235 }
236 Middle => true,
237 NewIntroPoint | ContinuingIntroPoint => relay.is_hs_intro_point(),
240 NewGuard | ContinuingGuard => relay.is_suitable_as_guard() && relay.is_dir_cache(),
243 #[cfg(feature = "vanguards")]
244 Vanguard => {
245 true
247 }
248 DirectoryCache => relay.is_dir_cache(),
249 }
250 }
251}
252
253#[cfg(test)]
254mod test {
255 #![allow(clippy::bool_assert_comparison)]
257 #![allow(clippy::clone_on_copy)]
258 #![allow(clippy::dbg_macro)]
259 #![allow(clippy::mixed_attributes_style)]
260 #![allow(clippy::print_stderr)]
261 #![allow(clippy::print_stdout)]
262 #![allow(clippy::single_char_pattern)]
263 #![allow(clippy::unwrap_used)]
264 #![allow(clippy::unchecked_duration_subtraction)]
265 #![allow(clippy::useless_vec)]
266 #![allow(clippy::needless_pass_by_value)]
267 use super::*;
270 use crate::testing::{cfg, split_netdir, testnet};
271
272 #[test]
273 fn any_exits() {
274 let nd = testnet();
275
276 let (yes, no) = split_netdir(&nd, &RelayUsage::any_exit(&cfg()));
277
278 let p = |r: &Relay<'_>| {
279 r.low_level_details().is_flagged_fast()
280 && r.low_level_details().policies_allow_some_port()
281 };
282 assert!(yes.iter().all(p));
283 assert!(no.iter().all(|r| !p(r)));
284 }
285
286 #[test]
287 fn all_ports() {
288 let nd = testnet();
289 let ports_stable = vec![TargetPort::ipv4(22), TargetPort::ipv4(80)];
290 let usage_stable = RelayUsage::exit_to_all_ports(&cfg(), ports_stable);
291 assert!(usage_stable.need_stable);
292
293 let p1 = |relay: &Relay<'_>| {
294 let r = relay.low_level_details();
295 r.is_flagged_fast()
296 && r.is_flagged_stable()
297 && r.ipv4_policy().allows_port(22)
298 && r.ipv4_policy().allows_port(80)
299 };
300
301 let (yes, no) = split_netdir(&nd, &usage_stable);
302 assert!(yes.iter().all(p1));
303 assert!(no.iter().all(|r| !p1(r)));
304
305 let ports_not_stable = vec![TargetPort::ipv4(80)];
306 let usage_not_stable = RelayUsage::exit_to_all_ports(&cfg(), ports_not_stable);
307
308 let p2 = |relay: &Relay<'_>| {
309 let r = relay.low_level_details();
310 r.is_flagged_fast() && r.ipv4_policy().allows_port(80)
311 };
312 let (yes, no) = split_netdir(&nd, &usage_not_stable);
313 assert!(yes.iter().all(p2));
314 assert!(no.iter().all(|r| !p2(r)));
315 }
316
317 #[test]
318 fn any_port() {
319 let nd = testnet();
320 let ports = vec![TargetPort::ipv4(22), TargetPort::ipv4(80)];
321 let usage = RelayUsage::exit_to_any_port(&cfg(), ports);
322 assert!(!usage.need_stable);
323 match &usage.inner {
324 RelayUsageInner::ExitToAnyPort {
325 stable_ports,
326 unstable_ports,
327 } => {
328 assert_eq!(&stable_ports[..], &[TargetPort::ipv4(22)]);
329 assert_eq!(&unstable_ports[..], &[TargetPort::ipv4(80)]);
330 }
331 _ => {
332 panic!("Wrong kind of usage.");
333 }
334 }
335
336 let p = |relay: &Relay<'_>| {
337 let r = relay.low_level_details();
338 let port_22 = r.is_flagged_stable() && r.ipv4_policy().allows_port(22);
339 let port_80 = r.ipv4_policy().allows_port(80);
340 r.is_flagged_fast() && (port_22 || port_80)
341 };
342
343 let (yes, no) = split_netdir(&nd, &usage);
344 assert!(yes.iter().all(p));
345 assert!(no.iter().all(|r| !p(r)));
346 }
347
348 #[test]
349 fn middle() {
350 let nd = testnet();
351
352 let u_unstable = RelayUsage::any_exit(&cfg());
353 let u_stable = RelayUsage::new_guard();
354 let mid_stable = RelayUsage::middle_relay(Some(&u_stable));
355 let mid_unstable = RelayUsage::middle_relay(Some(&u_unstable));
356 let mid_default = RelayUsage::middle_relay(None);
357 assert!(mid_stable.need_stable);
358 assert!(!mid_unstable.need_stable);
359 assert!(mid_default.need_stable);
360
361 let (yes, no) = split_netdir(&nd, &mid_unstable);
362 let p1 = |relay: &Relay<'_>| {
363 let r = relay.low_level_details();
364 r.is_flagged_fast()
365 };
366 assert!(yes.iter().all(p1));
367 assert!(no.iter().all(|r| !p1(r)));
368
369 let (yes, no) = split_netdir(&nd, &mid_stable);
370 let p2 = |relay: &Relay<'_>| {
371 let r = relay.low_level_details();
372 r.is_flagged_fast() && r.is_flagged_stable()
373 };
374 assert!(yes.iter().all(p2));
375 assert!(no.iter().all(|r| !p2(r)));
376 }
377
378 #[test]
379 fn intro() {
380 let nd = testnet();
381 let usage = RelayUsage::new_intro_point();
382
383 let (yes, no) = split_netdir(&nd, &usage);
384 let p1 = |relay: &Relay<'_>| {
385 let r = relay.low_level_details();
386 r.is_flagged_fast() && r.is_flagged_stable()
387 };
388 assert!(yes.iter().all(p1));
389 assert!(no.iter().all(|r| !p1(r)));
390 }
391
392 #[test]
393 fn guard() {
394 let nd = testnet();
395 let usage = RelayUsage::new_guard();
396
397 let (yes, no) = split_netdir(&nd, &usage);
398 let p1 = |relay: &Relay<'_>| {
399 let r = relay.low_level_details();
400 r.is_flagged_fast()
401 && r.is_flagged_stable()
402 && r.is_suitable_as_guard()
403 && r.is_dir_cache()
404 };
405 assert!(yes.iter().all(p1));
406 assert!(no.iter().all(|r| !p1(r)));
407 }
408
409 #[test]
410 fn cache() {
411 let nd = testnet();
412 let usage = RelayUsage::directory_cache();
413
414 let (yes, no) = split_netdir(&nd, &usage);
415 let p1 = |relay: &Relay<'_>| {
416 let r = relay.low_level_details();
417 r.is_flagged_fast() && r.is_dir_cache()
418 };
419 assert!(yes.iter().all(p1));
420 assert!(no.iter().all(|r| !p1(r)));
421 }
422}