1use tor_basic_utils::define_accessor_trait;
8use tor_config::impl_standard_builder;
9use tor_config::{define_list_builder_accessors, define_list_builder_helper, ConfigBuildError};
10use tor_guardmgr::{GuardFilter, GuardMgrConfig};
11
12use derive_builder::Builder;
13use serde::{Deserialize, Serialize};
14use tor_netdoc::types::policy::AddrPortPattern;
15use tor_relay_selection::RelaySelectionConfig;
16
17use std::collections::HashSet;
18use std::time::Duration;
19
20#[derive(Debug, Clone, Builder, Eq, PartialEq)]
30#[builder(build_fn(error = "ConfigBuildError"))]
31#[builder(derive(Debug, Serialize, Deserialize))]
32pub struct PathConfig {
33 #[builder(default = "ipv4_prefix_default()")]
38 ipv4_subnet_family_prefix: u8,
39
40 #[builder(default = "ipv6_prefix_default()")]
45 ipv6_subnet_family_prefix: u8,
46
47 #[builder(sub_builder, setter(custom))]
49 #[builder_field_attr(serde(default))]
50 pub(crate) long_lived_ports: LongLivedPorts,
51
52 #[builder(sub_builder, setter(custom))]
54 #[builder_field_attr(serde(default))]
55 pub(crate) reachable_addrs: ReachableAddrs,
56}
57impl_standard_builder! { PathConfig }
58
59type ReachableAddrs = Vec<AddrPortPattern>;
61
62fn default_reachable_addrs() -> ReachableAddrs {
64 vec![AddrPortPattern::new_all()]
65}
66
67define_list_builder_helper! {
68 struct ReachableAddrsBuilder {
69 pub(crate) patterns: [AddrPortPattern],
70 }
71 built: ReachableAddrs = patterns;
72 default = default_reachable_addrs();
73 item_build: |pat| Ok(pat.clone());
74}
75
76define_list_builder_accessors! {
77 struct PathConfigBuilder {
78 pub reachable_addrs: [AddrPortPattern],
79 }
80}
81
82type LongLivedPorts = HashSet<u16>;
84
85define_list_builder_helper! {
86 pub struct LongLivedPortsBuilder {
87 long_lived_ports:[u16],
88 }
89 built: LongLivedPorts = long_lived_ports;
90 default = long_lived_ports_default();
91 item_build: |item| Ok(*item);
92}
93
94define_list_builder_accessors! {
95 struct PathConfigBuilder {
96 pub long_lived_ports: [u16],
97 }
98}
99
100fn ipv4_prefix_default() -> u8 {
102 16
103}
104fn ipv6_prefix_default() -> u8 {
106 32
107}
108fn long_lived_ports_default() -> Vec<u16> {
110 vec![
111 21, 22, 706, 1863, 5050, 5190, 5222, 5223, 6523, 6667, 6697, 8300,
112 ]
113}
114
115impl PathConfig {
116 pub fn subnet_config(&self) -> tor_netdir::SubnetConfig {
118 tor_netdir::SubnetConfig::new(
119 self.ipv4_subnet_family_prefix,
120 self.ipv6_subnet_family_prefix,
121 )
122 }
123
124 pub(crate) fn at_least_as_permissive_as(&self, other: &Self) -> bool {
133 self.ipv4_subnet_family_prefix >= other.ipv4_subnet_family_prefix
134 && self.ipv6_subnet_family_prefix >= other.ipv6_subnet_family_prefix
135 && self.reachable_addrs == other.reachable_addrs
136 }
137
138 pub(crate) fn build_guard_filter(&self) -> GuardFilter {
140 let mut filt = GuardFilter::default();
141 filt.push_reachable_addresses(self.reachable_addrs.clone());
142 filt
143 }
144
145 pub(crate) fn relay_selection_config(&self) -> RelaySelectionConfig<'_> {
148 RelaySelectionConfig {
149 long_lived_ports: &self.long_lived_ports,
150 subnet_config: self.subnet_config(),
151 }
152 }
153}
154
155#[derive(Debug, Clone, Builder, Eq, PartialEq)]
166#[builder(build_fn(error = "ConfigBuildError"))]
167#[builder(derive(Debug, Serialize, Deserialize))]
168pub struct PreemptiveCircuitConfig {
169 #[builder(default = "default_preemptive_threshold()")]
173 pub(crate) disable_at_threshold: usize,
174
175 #[builder(sub_builder, setter(custom))]
185 pub(crate) initial_predicted_ports: PredictedPortsList,
186
187 #[builder(default = "default_preemptive_duration()")]
191 #[builder_field_attr(serde(default, with = "humantime_serde::option"))]
192 pub(crate) prediction_lifetime: Duration,
193
194 #[builder(default = "default_preemptive_min_exit_circs_for_port()")]
197 pub(crate) min_exit_circs_for_port: usize,
198}
199impl_standard_builder! { PreemptiveCircuitConfig }
200
201#[derive(Debug, Clone, Builder, Eq, PartialEq)]
212#[builder(build_fn(error = "ConfigBuildError"))]
213#[builder(derive(Debug, Serialize, Deserialize))]
214#[derive(amplify::Getters)]
218pub struct CircuitTiming {
219 #[builder(default = "default_max_dirtiness()")]
222 #[builder_field_attr(serde(default, with = "humantime_serde::option"))]
223 #[getter(skip)]
224 pub(crate) max_dirtiness: Duration,
225
226 #[builder(default = "default_request_timeout()")]
230 #[builder_field_attr(serde(default, with = "humantime_serde::option"))]
231 #[getter(skip)]
232 pub(crate) request_timeout: Duration,
233
234 #[builder(default = "default_request_max_retries()")]
238 #[getter(skip)]
239 pub(crate) request_max_retries: u32,
240
241 #[builder(default = "default_request_loyalty()")]
245 #[builder_field_attr(serde(default, with = "humantime_serde::option"))]
246 #[getter(skip)]
247 pub(crate) request_loyalty: Duration,
248
249 #[cfg(feature = "hs-client")]
255 #[builder(default = "default_hs_max_attempts()")]
256 #[getter(as_copy)]
257 pub(crate) hs_desc_fetch_attempts: u32,
258
259 #[cfg(feature = "hs-client")]
264 #[builder(default = "default_hs_max_attempts()")]
265 #[getter(as_copy)]
266 pub(crate) hs_intro_rend_attempts: u32,
267}
268impl_standard_builder! { CircuitTiming }
269
270fn default_preemptive_threshold() -> usize {
272 12
273}
274
275type PredictedPortsList = Vec<u16>;
277
278define_list_builder_helper! {
279 struct PredictedPortsListBuilder {
280 pub(crate) ports: [u16],
281 }
282 built: PredictedPortsList = ports;
283 default = default_preemptive_ports();
284 item_build: |&port| Ok(port);
285}
286
287define_list_builder_accessors! {
288 struct PreemptiveCircuitConfigBuilder {
289 pub initial_predicted_ports: [u16],
290 }
291}
292
293fn default_preemptive_ports() -> Vec<u16> {
295 vec![80, 443]
296}
297
298fn default_preemptive_duration() -> Duration {
300 Duration::from_secs(60 * 60)
301}
302
303fn default_preemptive_min_exit_circs_for_port() -> usize {
305 2
306}
307
308fn default_max_dirtiness() -> Duration {
310 Duration::from_secs(60 * 10)
311}
312
313fn default_request_timeout() -> Duration {
315 Duration::from_secs(60)
316}
317
318fn default_request_max_retries() -> u32 {
320 16
321}
322
323#[cfg(feature = "hs-client")]
325fn default_hs_max_attempts() -> u32 {
326 6
330}
331
332fn default_request_loyalty() -> Duration {
334 Duration::from_millis(50)
335}
336
337define_accessor_trait! {
338 pub trait CircMgrConfig: GuardMgrConfig {
366 path_rules: PathConfig,
367 circuit_timing: CircuitTiming,
368 preemptive_circuits: PreemptiveCircuitConfig,
369 +
370 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
377 fn vanguard_config(&self) -> &tor_guardmgr::VanguardConfig;
378 }
379}
380
381#[cfg(any(test, feature = "testing"))]
383pub(crate) mod test_config {
384 use super::*;
385 use crate::*;
386 use tor_guardmgr::bridge::BridgeConfig;
387 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
388 use tor_guardmgr::VanguardConfig;
389
390 #[derive(Default, derive_more::AsRef)]
392 #[allow(clippy::exhaustive_structs)]
393 #[allow(missing_docs)]
394 #[cfg_attr(docsrs, doc(cfg(feature = "testing")))]
395 pub struct TestConfig {
396 pub path_rules: PathConfig,
397 pub circuit_timing: CircuitTiming,
398 pub preemptive_circuits: PreemptiveCircuitConfig,
399 pub guardmgr: tor_guardmgr::TestConfig,
400 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
401 pub vanguard_config: VanguardConfig,
402 }
403 impl AsRef<[BridgeConfig]> for TestConfig {
404 fn as_ref(&self) -> &[BridgeConfig] {
405 &self.guardmgr.bridges
406 }
407 }
408 impl AsRef<FallbackList> for TestConfig {
409 fn as_ref(&self) -> &FallbackList {
410 &self.guardmgr.fallbacks
411 }
412 }
413 impl GuardMgrConfig for TestConfig {
414 fn bridges_enabled(&self) -> bool {
415 self.guardmgr.bridges_enabled()
416 }
417 }
418 impl CircMgrConfig for TestConfig {
419 fn path_rules(&self) -> &PathConfig {
420 &self.path_rules
421 }
422 fn circuit_timing(&self) -> &CircuitTiming {
423 &self.circuit_timing
424 }
425 fn preemptive_circuits(&self) -> &PreemptiveCircuitConfig {
426 &self.preemptive_circuits
427 }
428 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
429 fn vanguard_config(&self) -> &tor_guardmgr::VanguardConfig {
430 &self.vanguard_config
431 }
432 }
433}
434
435#[cfg(test)]
436mod test {
437 #![allow(clippy::bool_assert_comparison)]
439 #![allow(clippy::clone_on_copy)]
440 #![allow(clippy::dbg_macro)]
441 #![allow(clippy::mixed_attributes_style)]
442 #![allow(clippy::print_stderr)]
443 #![allow(clippy::print_stdout)]
444 #![allow(clippy::single_char_pattern)]
445 #![allow(clippy::unwrap_used)]
446 #![allow(clippy::unchecked_duration_subtraction)]
447 #![allow(clippy::useless_vec)]
448 #![allow(clippy::needless_pass_by_value)]
449 use super::*;
451
452 #[test]
453 fn path_config() {
454 let pc1 = PathConfig::default();
455 let pc2 = PathConfig::builder()
459 .ipv4_subnet_family_prefix(32)
460 .build()
461 .unwrap();
462 let pc3 = PathConfig::builder()
463 .ipv6_subnet_family_prefix(128)
464 .build()
465 .unwrap();
466
467 assert!(pc2.at_least_as_permissive_as(&pc1));
468 assert!(pc3.at_least_as_permissive_as(&pc1));
469 assert!(pc1.at_least_as_permissive_as(&pc1));
470 assert!(!pc1.at_least_as_permissive_as(&pc2));
471 assert!(!pc1.at_least_as_permissive_as(&pc3));
472 assert!(!pc3.at_least_as_permissive_as(&pc2));
473 }
474}