use derive_builder::Builder;
use serde::Deserialize;
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
use std::time::Duration;
pub use tor_config::{CfgPath, ConfigBuildError, Reconfigure};
pub mod circ {
pub use tor_circmgr::{
CircMgrConfig, CircMgrConfigBuilder, CircuitTiming, CircuitTimingBuilder, PathConfig,
PathConfigBuilder, PreemptiveCircuitConfig, PreemptiveCircuitConfigBuilder,
};
}
pub mod dir {
pub use tor_dirmgr::{
Authority, AuthorityBuilder, DirMgrConfig, DirMgrConfigBuilder, DownloadSchedule,
DownloadScheduleConfig, DownloadScheduleConfigBuilder, FallbackDir, FallbackDirBuilder,
NetworkConfig, NetworkConfigBuilder,
};
}
#[derive(Debug, Clone, Builder, Deserialize, Eq, PartialEq)]
#[builder(build_fn(error = "ConfigBuildError"))]
#[serde(deny_unknown_fields)]
pub struct ClientAddrConfig {
#[builder(default)]
#[serde(default)]
pub(crate) allow_local_addrs: bool,
}
#[derive(Debug, Clone, Builder, Deserialize, Eq, PartialEq)]
#[builder(build_fn(error = "ConfigBuildError"))]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct StreamTimeoutConfig {
#[builder(default = "default_connect_timeout()")]
#[serde(with = "humantime_serde", default = "default_connect_timeout")]
pub(crate) connect_timeout: Duration,
#[builder(default = "default_dns_resolve_timeout()")]
#[serde(with = "humantime_serde", default = "default_dns_resolve_timeout")]
pub(crate) resolve_timeout: Duration,
#[builder(default = "default_dns_resolve_ptr_timeout()")]
#[serde(with = "humantime_serde", default = "default_dns_resolve_ptr_timeout")]
pub(crate) resolve_ptr_timeout: Duration,
}
#[allow(clippy::unwrap_used)]
impl Default for ClientAddrConfig {
fn default() -> Self {
ClientAddrConfigBuilder::default().build().unwrap()
}
}
impl From<ClientAddrConfig> for ClientAddrConfigBuilder {
fn from(cfg: ClientAddrConfig) -> ClientAddrConfigBuilder {
let mut builder = ClientAddrConfigBuilder::default();
builder.allow_local_addrs(cfg.allow_local_addrs);
builder
}
}
impl ClientAddrConfig {
pub fn builder() -> ClientAddrConfigBuilder {
ClientAddrConfigBuilder::default()
}
}
#[allow(clippy::unwrap_used)]
impl Default for StreamTimeoutConfig {
fn default() -> Self {
StreamTimeoutConfigBuilder::default().build().unwrap()
}
}
impl From<StreamTimeoutConfig> for StreamTimeoutConfigBuilder {
fn from(cfg: StreamTimeoutConfig) -> StreamTimeoutConfigBuilder {
let mut builder = StreamTimeoutConfigBuilder::default();
builder
.connect_timeout(cfg.connect_timeout)
.resolve_timeout(cfg.resolve_timeout)
.resolve_ptr_timeout(cfg.resolve_ptr_timeout);
builder
}
}
impl StreamTimeoutConfig {
pub fn builder() -> StreamTimeoutConfigBuilder {
StreamTimeoutConfigBuilder::default()
}
}
fn default_connect_timeout() -> Duration {
Duration::new(10, 0)
}
fn default_dns_resolve_timeout() -> Duration {
Duration::new(10, 0)
}
fn default_dns_resolve_ptr_timeout() -> Duration {
Duration::new(10, 0)
}
#[derive(Deserialize, Debug, Clone, Builder, Eq, PartialEq)]
#[serde(deny_unknown_fields)]
#[builder(build_fn(error = "ConfigBuildError"))]
pub struct StorageConfig {
#[builder(setter(into), default = "default_cache_dir()")]
#[serde(default = "default_cache_dir")]
cache_dir: CfgPath,
#[builder(setter(into), default = "default_state_dir()")]
#[serde(default = "default_state_dir")]
state_dir: CfgPath,
}
fn default_cache_dir() -> CfgPath {
CfgPath::new("${ARTI_CACHE}".to_owned())
}
fn default_state_dir() -> CfgPath {
CfgPath::new("${ARTI_LOCAL_DATA}".to_owned())
}
impl Default for StorageConfig {
fn default() -> Self {
Self::builder().build().expect("Default builder failed")
}
}
impl StorageConfig {
pub fn builder() -> StorageConfigBuilder {
StorageConfigBuilder::default()
}
pub(crate) fn expand_state_dir(&self) -> Result<PathBuf, ConfigBuildError> {
self.state_dir
.path()
.map_err(|e| ConfigBuildError::Invalid {
field: "state_dir".to_owned(),
problem: e.to_string(),
})
}
pub(crate) fn expand_cache_dir(&self) -> Result<PathBuf, ConfigBuildError> {
self.cache_dir
.path()
.map_err(|e| ConfigBuildError::Invalid {
field: "cache_dir".to_owned(),
problem: e.to_string(),
})
}
}
impl From<StorageConfig> for StorageConfigBuilder {
fn from(cfg: StorageConfig) -> StorageConfigBuilder {
let mut builder = StorageConfigBuilder::default();
builder.state_dir(cfg.state_dir).cache_dir(cfg.cache_dir);
builder
}
}
#[derive(Deserialize, Debug, Clone, Builder, Eq, PartialEq)]
#[serde(deny_unknown_fields)]
#[builder(build_fn(error = "ConfigBuildError"))]
#[non_exhaustive]
pub struct SystemConfig {
#[builder(setter(into), default = "default_max_files()")]
#[serde(default = "default_max_files")]
pub max_files: u64,
}
fn default_max_files() -> u64 {
16384
}
impl Default for SystemConfig {
fn default() -> Self {
Self::builder().build().expect("Default builder failed")
}
}
impl SystemConfig {
pub fn builder() -> SystemConfigBuilder {
SystemConfigBuilder::default()
}
}
impl From<SystemConfig> for SystemConfigBuilder {
fn from(cfg: SystemConfig) -> SystemConfigBuilder {
let mut builder = SystemConfigBuilder::default();
builder.max_files(cfg.max_files);
builder
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TorClientConfig {
tor_network: dir::NetworkConfig,
pub(crate) storage: StorageConfig,
download_schedule: dir::DownloadScheduleConfig,
override_net_params: HashMap<String, i32>,
path_rules: circ::PathConfig,
preemptive_circuits: circ::PreemptiveCircuitConfig,
circuit_timing: circ::CircuitTiming,
pub(crate) address_filter: ClientAddrConfig,
pub(crate) stream_timeouts: StreamTimeoutConfig,
pub system: SystemConfig,
}
impl Default for TorClientConfig {
fn default() -> Self {
Self::builder()
.build()
.expect("Could not build TorClientConfig from default configuration.")
}
}
impl TorClientConfig {
pub fn builder() -> TorClientConfigBuilder {
TorClientConfigBuilder::default()
}
pub(crate) fn get_dirmgr_config(&self) -> Result<dir::DirMgrConfig, ConfigBuildError> {
let mut dircfg = dir::DirMgrConfigBuilder::default();
dircfg.network_config(self.tor_network.clone());
dircfg.schedule_config(self.download_schedule.clone());
dircfg.cache_path(self.storage.expand_cache_dir()?);
for (k, v) in &self.override_net_params {
dircfg.override_net_param(k.clone(), *v);
}
dircfg.build()
}
pub(crate) fn get_circmgr_config(&self) -> Result<circ::CircMgrConfig, ConfigBuildError> {
let mut builder = circ::CircMgrConfigBuilder::default();
builder
.path_rules(self.path_rules.clone())
.circuit_timing(self.circuit_timing.clone())
.build()
}
}
#[derive(Clone, Default)]
pub struct TorClientConfigBuilder {
tor_network: dir::NetworkConfigBuilder,
storage: StorageConfigBuilder,
download_schedule: dir::DownloadScheduleConfigBuilder,
override_net_params: HashMap<String, i32>,
path_rules: circ::PathConfigBuilder,
preemptive_circuits: circ::PreemptiveCircuitConfigBuilder,
circuit_timing: circ::CircuitTimingBuilder,
address_filter: ClientAddrConfigBuilder,
stream_timeouts: StreamTimeoutConfigBuilder,
system: SystemConfigBuilder,
}
impl TorClientConfigBuilder {
pub fn build(&self) -> Result<TorClientConfig, ConfigBuildError> {
let tor_network = self
.tor_network
.build()
.map_err(|e| e.within("tor_network"))?;
let storage = self.storage.build().map_err(|e| e.within("storage"))?;
let download_schedule = self
.download_schedule
.build()
.map_err(|e| e.within("download_schedule"))?;
let override_net_params = self.override_net_params.clone();
let path_rules = self
.path_rules
.build()
.map_err(|e| e.within("path_rules"))?;
let preemptive_circuits = self
.preemptive_circuits
.build()
.map_err(|e| e.within("preemptive_circuits"))?;
let circuit_timing = self
.circuit_timing
.build()
.map_err(|e| e.within("circuit_timing"))?;
let address_filter = self
.address_filter
.build()
.map_err(|e| e.within("address_filter"))?;
let stream_timeouts = self
.stream_timeouts
.build()
.map_err(|e| e.within("stream_timeouts"))?;
let system = self.system.build().map_err(|e| e.within("system"))?;
Ok(TorClientConfig {
tor_network,
storage,
download_schedule,
override_net_params,
path_rules,
preemptive_circuits,
circuit_timing,
address_filter,
stream_timeouts,
system,
})
}
pub fn from_directories<P, Q>(state_dir: P, cache_dir: Q) -> Self
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let mut builder = Self::default();
builder
.storage()
.cache_dir(CfgPath::from_path(cache_dir))
.state_dir(CfgPath::from_path(state_dir));
builder
}
pub fn tor_network(&mut self) -> &mut dir::NetworkConfigBuilder {
&mut self.tor_network
}
pub fn storage(&mut self) -> &mut StorageConfigBuilder {
&mut self.storage
}
pub fn download_schedule(&mut self) -> &mut dir::DownloadScheduleConfigBuilder {
&mut self.download_schedule
}
pub fn override_net_params(&mut self) -> &mut HashMap<String, i32> {
&mut self.override_net_params
}
pub fn path_rules(&mut self) -> &mut circ::PathConfigBuilder {
&mut self.path_rules
}
pub fn preemptive_circuits(&mut self) -> &mut circ::PreemptiveCircuitConfigBuilder {
&mut self.preemptive_circuits
}
pub fn circuit_timing(&mut self) -> &mut circ::CircuitTimingBuilder {
&mut self.circuit_timing
}
pub fn stream_timeouts(&mut self) -> &mut StreamTimeoutConfigBuilder {
&mut self.stream_timeouts
}
pub fn address_filter(&mut self) -> &mut ClientAddrConfigBuilder {
&mut self.address_filter
}
pub fn system(&mut self) -> &mut SystemConfigBuilder {
&mut self.system
}
}
impl From<TorClientConfig> for TorClientConfigBuilder {
fn from(cfg: TorClientConfig) -> TorClientConfigBuilder {
let TorClientConfig {
tor_network,
storage,
download_schedule,
override_net_params,
path_rules,
preemptive_circuits,
circuit_timing,
address_filter,
stream_timeouts,
system,
} = cfg;
TorClientConfigBuilder {
tor_network: tor_network.into(),
storage: storage.into(),
download_schedule: download_schedule.into(),
override_net_params,
path_rules: path_rules.into(),
preemptive_circuits: preemptive_circuits.into(),
circuit_timing: circuit_timing.into(),
address_filter: address_filter.into(),
stream_timeouts: stream_timeouts.into(),
system: system.into(),
}
}
}
#[cfg(test)]
mod test {
#![allow(clippy::unwrap_used)]
use super::*;
#[test]
fn defaults() {
let dflt = TorClientConfig::default();
let b2 = TorClientConfigBuilder::from(dflt.clone());
let dflt2 = b2.build().unwrap();
assert_eq!(&dflt, &dflt2);
}
#[test]
fn builder() {
use tor_dirmgr::DownloadSchedule;
let sec = std::time::Duration::from_secs(1);
let auth = dir::Authority::builder()
.name("Fred")
.v3ident([22; 20].into())
.build()
.unwrap();
let fallback = dir::FallbackDir::builder()
.rsa_identity([23; 20].into())
.ed_identity([99; 32].into())
.orports(vec!["127.0.0.7:7".parse().unwrap()])
.build()
.unwrap();
let mut bld = TorClientConfig::builder();
bld.tor_network()
.authorities(vec![auth])
.fallback_caches(vec![fallback]);
bld.storage()
.cache_dir(CfgPath::new("/var/tmp/foo".to_owned()))
.state_dir(CfgPath::new("/var/tmp/bar".to_owned()));
bld.download_schedule()
.retry_certs(DownloadSchedule::new(10, sec, 3))
.retry_microdescs(DownloadSchedule::new(30, 10 * sec, 9));
bld.override_net_params()
.insert("wombats-per-quokka".to_owned(), 7);
bld.path_rules()
.ipv4_subnet_family_prefix(20)
.ipv6_subnet_family_prefix(48);
bld.circuit_timing()
.max_dirtiness(90 * sec)
.request_timeout(10 * sec)
.request_max_retries(22)
.request_loyalty(3600 * sec);
bld.address_filter().allow_local_addrs(true);
let val = bld.build().unwrap();
let bld2 = TorClientConfigBuilder::from(val.clone());
let val2 = bld2.build().unwrap();
assert_eq!(val, val2);
assert_ne!(val, TorClientConfig::default());
}
}