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 NewRendPoint,
57}
58
59impl RelayUsage {
60 pub fn any_exit(_cfg: &RelaySelectionConfig) -> Self {
66 RelayUsage {
72 inner: RelayUsageInner::AnyExit,
73 need_stable: false,
74 }
75 }
76
77 pub fn exit_to_all_ports(cfg: &RelaySelectionConfig, ports: Vec<TargetPort>) -> Self {
79 let need_stable = ports.iter().any(|p| cfg.port_requires_stable_flag(p.port));
80 RelayUsage {
81 inner: RelayUsageInner::ExitToAllPorts(ports),
82 need_stable,
83 }
84 }
85
86 pub fn exit_to_any_port(cfg: &RelaySelectionConfig, ports: Vec<TargetPort>) -> Self {
88 let (stable_ports, unstable_ports): (Vec<_>, Vec<_>) = ports
89 .into_iter()
90 .partition(|p| cfg.port_requires_stable_flag(p.port));
91 let need_stable = unstable_ports.is_empty() && !stable_ports.is_empty();
92 RelayUsage {
93 inner: RelayUsageInner::ExitToAnyPort {
94 stable_ports,
95 unstable_ports,
96 },
97 need_stable,
98 }
99 }
100
101 pub fn middle_relay(known_final_hop_usage: Option<&RelayUsage>) -> Self {
113 let need_stable = known_final_hop_usage.map(|u| u.need_stable).unwrap_or(true);
114 RelayUsage {
115 inner: RelayUsageInner::Middle,
116 need_stable,
117 }
118 }
119
120 pub fn new_intro_point() -> Self {
126 RelayUsage {
127 inner: RelayUsageInner::NewIntroPoint,
128 need_stable: true,
129 }
130 }
131
132 pub fn continuing_intro_point() -> Self {
134 RelayUsage {
135 inner: RelayUsageInner::ContinuingIntroPoint,
136 need_stable: true,
137 }
138 }
139
140 pub fn new_guard() -> Self {
146 RelayUsage {
147 inner: RelayUsageInner::NewGuard,
148 need_stable: true,
149 }
150 }
151
152 pub fn continuing_guard() -> Self {
154 RelayUsage {
155 inner: RelayUsageInner::ContinuingGuard,
156 need_stable: true,
157 }
158 }
159
160 #[cfg(feature = "vanguards")]
162 pub fn vanguard() -> Self {
163 RelayUsage {
164 inner: RelayUsageInner::Vanguard,
165 need_stable: true,
167 }
168 }
169
170 pub fn directory_cache() -> Self {
176 RelayUsage {
177 inner: RelayUsageInner::DirectoryCache,
178 need_stable: false,
179 }
180 }
181
182 pub fn new_rend_point() -> Self {
185 RelayUsage {
186 inner: RelayUsageInner::NewRendPoint,
187 need_stable: true,
188 }
189 }
190
191 pub(crate) fn selection_weight_role(&self) -> WeightRole {
193 use RelayUsageInner::*;
194
195 match &self.inner {
196 AnyExit | ExitToAllPorts(_) | ExitToAnyPort { .. } => WeightRole::Exit,
197 Middle => WeightRole::Middle,
198 NewIntroPoint | ContinuingIntroPoint => WeightRole::HsIntro,
199 NewGuard | ContinuingGuard => WeightRole::Guard,
200 #[cfg(feature = "vanguards")]
201 Vanguard => WeightRole::Middle,
202 DirectoryCache => WeightRole::BeginDir,
203 NewRendPoint => WeightRole::HsRend,
204 }
205 }
206
207 pub(crate) fn rejection_description(&self) -> &'static str {
210 use RelayUsageInner::*;
211 match &self.inner {
212 AnyExit => "non-exit",
213 ExitToAllPorts(_) => "not exiting to desired ports",
214 ExitToAnyPort { .. } => "not exiting to any desired port",
215 Middle => "not usable as middle relay",
216 NewIntroPoint | ContinuingIntroPoint => "not introduction point",
217 NewGuard | ContinuingGuard => "not guard",
218 #[cfg(feature = "vanguards")]
219 Vanguard => "not usable as vanguard",
220 DirectoryCache => "not directory cache",
221 NewRendPoint => "not usable as rendezvous point",
222 }
223 }
224}
225
226impl LowLevelRelayPredicate for RelayUsage {
227 fn low_level_predicate_permits_relay(&self, relay_in: &Relay<'_>) -> bool {
228 use RelayUsageInner::*;
229 let relay = relay_in.low_level_details();
230 if !relay.is_flagged_fast() {
231 return false;
232 }
233 if self.need_stable && !relay.is_flagged_stable() {
234 return false;
235 }
236 match &self.inner {
237 AnyExit => relay.policies_allow_some_port(),
238 ExitToAllPorts(ports) => ports.iter().all(|p| p.is_supported_by(&relay)),
239 ExitToAnyPort {
240 stable_ports,
241 unstable_ports,
242 } => {
243 if relay.is_flagged_stable()
244 && stable_ports.iter().any(|p| p.is_supported_by(&relay))
245 {
246 return true;
247 }
248 unstable_ports.iter().any(|p| p.is_supported_by(&relay))
249 }
250 Middle => true,
251 NewIntroPoint | ContinuingIntroPoint => relay.is_hs_intro_point(),
254 NewGuard | ContinuingGuard => relay.is_suitable_as_guard() && relay.is_dir_cache(),
257 #[cfg(feature = "vanguards")]
258 Vanguard => {
259 true
261 }
262 DirectoryCache => relay.is_dir_cache(),
263 NewRendPoint => relay.is_hs_rend_point(),
264 }
265 }
266}
267
268#[cfg(test)]
269mod test {
270 #![allow(clippy::bool_assert_comparison)]
272 #![allow(clippy::clone_on_copy)]
273 #![allow(clippy::dbg_macro)]
274 #![allow(clippy::mixed_attributes_style)]
275 #![allow(clippy::print_stderr)]
276 #![allow(clippy::print_stdout)]
277 #![allow(clippy::single_char_pattern)]
278 #![allow(clippy::unwrap_used)]
279 #![allow(clippy::unchecked_duration_subtraction)]
280 #![allow(clippy::useless_vec)]
281 #![allow(clippy::needless_pass_by_value)]
282 use super::*;
285 use crate::testing::{cfg, split_netdir, testnet};
286
287 #[test]
288 fn any_exits() {
289 let nd = testnet();
290
291 let (yes, no) = split_netdir(&nd, &RelayUsage::any_exit(&cfg()));
292
293 let p = |r: &Relay<'_>| {
294 r.low_level_details().is_flagged_fast()
295 && r.low_level_details().policies_allow_some_port()
296 };
297 assert!(yes.iter().all(p));
298 assert!(no.iter().all(|r| !p(r)));
299 }
300
301 #[test]
302 fn all_ports() {
303 let nd = testnet();
304 let ports_stable = vec![TargetPort::ipv4(22), TargetPort::ipv4(80)];
305 let usage_stable = RelayUsage::exit_to_all_ports(&cfg(), ports_stable);
306 assert!(usage_stable.need_stable);
307
308 let p1 = |relay: &Relay<'_>| {
309 let r = relay.low_level_details();
310 r.is_flagged_fast()
311 && r.is_flagged_stable()
312 && r.ipv4_policy().allows_port(22)
313 && r.ipv4_policy().allows_port(80)
314 };
315
316 let (yes, no) = split_netdir(&nd, &usage_stable);
317 assert!(yes.iter().all(p1));
318 assert!(no.iter().all(|r| !p1(r)));
319
320 let ports_not_stable = vec![TargetPort::ipv4(80)];
321 let usage_not_stable = RelayUsage::exit_to_all_ports(&cfg(), ports_not_stable);
322
323 let p2 = |relay: &Relay<'_>| {
324 let r = relay.low_level_details();
325 r.is_flagged_fast() && r.ipv4_policy().allows_port(80)
326 };
327 let (yes, no) = split_netdir(&nd, &usage_not_stable);
328 assert!(yes.iter().all(p2));
329 assert!(no.iter().all(|r| !p2(r)));
330 }
331
332 #[test]
333 fn any_port() {
334 let nd = testnet();
335 let ports = vec![TargetPort::ipv4(22), TargetPort::ipv4(80)];
336 let usage = RelayUsage::exit_to_any_port(&cfg(), ports);
337 assert!(!usage.need_stable);
338 match &usage.inner {
339 RelayUsageInner::ExitToAnyPort {
340 stable_ports,
341 unstable_ports,
342 } => {
343 assert_eq!(&stable_ports[..], &[TargetPort::ipv4(22)]);
344 assert_eq!(&unstable_ports[..], &[TargetPort::ipv4(80)]);
345 }
346 _ => {
347 panic!("Wrong kind of usage.");
348 }
349 }
350
351 let p = |relay: &Relay<'_>| {
352 let r = relay.low_level_details();
353 let port_22 = r.is_flagged_stable() && r.ipv4_policy().allows_port(22);
354 let port_80 = r.ipv4_policy().allows_port(80);
355 r.is_flagged_fast() && (port_22 || port_80)
356 };
357
358 let (yes, no) = split_netdir(&nd, &usage);
359 assert!(yes.iter().all(p));
360 assert!(no.iter().all(|r| !p(r)));
361 }
362
363 #[test]
364 fn middle() {
365 let nd = testnet();
366
367 let u_unstable = RelayUsage::any_exit(&cfg());
368 let u_stable = RelayUsage::new_guard();
369 let mid_stable = RelayUsage::middle_relay(Some(&u_stable));
370 let mid_unstable = RelayUsage::middle_relay(Some(&u_unstable));
371 let mid_default = RelayUsage::middle_relay(None);
372 assert!(mid_stable.need_stable);
373 assert!(!mid_unstable.need_stable);
374 assert!(mid_default.need_stable);
375
376 let (yes, no) = split_netdir(&nd, &mid_unstable);
377 let p1 = |relay: &Relay<'_>| {
378 let r = relay.low_level_details();
379 r.is_flagged_fast()
380 };
381 assert!(yes.iter().all(p1));
382 assert!(no.iter().all(|r| !p1(r)));
383
384 let (yes, no) = split_netdir(&nd, &mid_stable);
385 let p2 = |relay: &Relay<'_>| {
386 let r = relay.low_level_details();
387 r.is_flagged_fast() && r.is_flagged_stable()
388 };
389 assert!(yes.iter().all(p2));
390 assert!(no.iter().all(|r| !p2(r)));
391 }
392
393 #[test]
394 fn intro() {
395 let nd = testnet();
396 let usage = RelayUsage::new_intro_point();
397
398 let (yes, no) = split_netdir(&nd, &usage);
399 let p1 = |relay: &Relay<'_>| {
400 let r = relay.low_level_details();
401 r.is_flagged_fast() && r.is_flagged_stable()
402 };
403 assert!(yes.iter().all(p1));
404 assert!(no.iter().all(|r| !p1(r)));
405 }
406
407 #[test]
408 fn guard() {
409 let nd = testnet();
410 let usage = RelayUsage::new_guard();
411
412 let (yes, no) = split_netdir(&nd, &usage);
413 let p1 = |relay: &Relay<'_>| {
414 let r = relay.low_level_details();
415 r.is_flagged_fast()
416 && r.is_flagged_stable()
417 && r.is_suitable_as_guard()
418 && r.is_dir_cache()
419 };
420 assert!(yes.iter().all(p1));
421 assert!(no.iter().all(|r| !p1(r)));
422 }
423
424 #[test]
425 fn cache() {
426 let nd = testnet();
427 let usage = RelayUsage::directory_cache();
428
429 let (yes, no) = split_netdir(&nd, &usage);
430 let p1 = |relay: &Relay<'_>| {
431 let r = relay.low_level_details();
432 r.is_flagged_fast() && r.is_dir_cache()
433 };
434 assert!(yes.iter().all(p1));
435 assert!(no.iter().all(|r| !p1(r)));
436 }
437}