#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#![allow(renamed_and_removed_lints)] #![allow(unknown_lints)] #![warn(missing_docs)]
#![warn(noop_method_call)]
#![warn(unreachable_pub)]
#![warn(clippy::all)]
#![deny(clippy::await_holding_lock)]
#![deny(clippy::cargo_common_metadata)]
#![deny(clippy::cast_lossless)]
#![deny(clippy::checked_conversions)]
#![warn(clippy::cognitive_complexity)]
#![deny(clippy::debug_assert_with_mut_call)]
#![deny(clippy::exhaustive_enums)]
#![deny(clippy::exhaustive_structs)]
#![deny(clippy::expl_impl_clone_on_copy)]
#![deny(clippy::fallible_impl_from)]
#![deny(clippy::implicit_clone)]
#![deny(clippy::large_stack_arrays)]
#![warn(clippy::manual_ok_or)]
#![deny(clippy::missing_docs_in_private_items)]
#![warn(clippy::needless_borrow)]
#![warn(clippy::needless_pass_by_value)]
#![warn(clippy::option_option)]
#![deny(clippy::print_stderr)]
#![deny(clippy::print_stdout)]
#![warn(clippy::rc_buffer)]
#![deny(clippy::ref_option_ref)]
#![warn(clippy::semicolon_if_nothing_returned)]
#![warn(clippy::trait_duplication_in_bounds)]
#![deny(clippy::unchecked_time_subtraction)]
#![deny(clippy::unnecessary_wraps)]
#![warn(clippy::unseparated_literal_suffix)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::mod_module_files)]
#![allow(clippy::let_unit_value)] #![allow(clippy::uninlined_format_args)]
#![allow(clippy::significant_drop_in_scrutinee)] #![allow(clippy::result_large_err)] #![allow(clippy::needless_raw_string_hashes)] #![allow(clippy::needless_lifetimes)] #![allow(mismatched_lifetime_syntaxes)] #![allow(clippy::collapsible_if)] #![deny(clippy::unused_async)]
#![cfg_attr(not(all(feature = "full")), allow(unused))]
pub mod auth;
#[cfg(feature = "rpc-client")]
pub mod client;
mod connpt;
pub mod load;
#[cfg(feature = "rpc-server")]
pub mod server;
#[cfg(test)]
mod testing;
use std::{io, sync::Arc};
pub use connpt::{ParsedConnectPoint, ResolveError, ResolvedConnectPoint};
use tor_general_addr::general;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[allow(clippy::exhaustive_enums)]
pub enum ClientErrorAction {
Abort,
Decline,
}
pub trait HasClientErrorAction {
fn client_action(&self) -> ClientErrorAction;
}
impl HasClientErrorAction for tor_config_path::CfgPathError {
fn client_action(&self) -> ClientErrorAction {
ClientErrorAction::Abort
}
}
impl HasClientErrorAction for tor_config_path::addr::CfgAddrError {
fn client_action(&self) -> ClientErrorAction {
use ClientErrorAction as A;
use tor_config_path::addr::CfgAddrError as CAE;
match self {
CAE::NoAfUnixSocketSupport(_) => A::Decline,
CAE::Path(cfg_path_error) => cfg_path_error.client_action(),
CAE::ConstructAfUnixAddress(_) => A::Abort,
_ => A::Abort,
}
}
}
impl HasClientErrorAction for tor_general_addr::general::AddrParseError {
fn client_action(&self) -> ClientErrorAction {
use ClientErrorAction as A;
use tor_general_addr::general::AddrParseError as E;
match self {
E::UnrecognizedSchema(_) => A::Decline,
E::NoSchema => A::Decline,
E::InvalidAfUnixAddress(_) => A::Abort,
E::InvalidInetAddress(_) => A::Decline,
_ => A::Abort,
}
}
}
fn fs_error_action(err: &std::io::Error) -> ClientErrorAction {
use ClientErrorAction as A;
use std::io::ErrorKind as EK;
match err.kind() {
EK::NotFound => A::Decline,
EK::PermissionDenied => A::Decline,
_ => A::Abort,
}
}
fn net_error_action(err: &std::io::Error) -> ClientErrorAction {
use ClientErrorAction as A;
use std::io::ErrorKind as EK;
match err.kind() {
EK::ConnectionRefused => A::Decline,
EK::ConnectionReset => A::Decline,
_ => A::Abort,
}
}
impl HasClientErrorAction for fs_mistrust::Error {
fn client_action(&self) -> ClientErrorAction {
use ClientErrorAction as A;
use fs_mistrust::Error as E;
match self {
E::Multiple(errs) => {
if errs.iter().any(|e| e.client_action() == A::Abort) {
A::Abort
} else {
A::Decline
}
}
E::Io { err, .. } => fs_error_action(err),
E::CouldNotInspect(_, err) => fs_error_action(err),
E::NotFound(_) => A::Decline,
E::BadPermission(_, _, _) | E::BadOwner(_, _) => A::Decline,
E::StepsExceeded | E::CurrentDirectory(_) => A::Abort,
E::BadType(_) => A::Abort,
E::CreatingDir(_)
| E::Content(_)
| E::NoSuchGroup(_)
| E::NoSuchUser(_)
| E::MissingField(_)
| E::InvalidSubdirectory => A::Abort,
E::PasswdGroupIoError(_) => A::Abort,
_ => A::Abort,
}
}
}
#[derive(Clone, Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ConnectError {
#[error("IO error while connecting")]
Io(#[source] Arc<io::Error>),
#[error("Encountered an explicit \"abort\"")]
ExplicitAbort,
#[error("Unable to load cookie file")]
LoadCookie(#[from] auth::cookie::CookieAccessError),
#[error("Unsupported socket type")]
UnsupportedSocketType,
#[error("Unsupported authentication type")]
UnsupportedAuthType,
#[error("Unix domain socket path access")]
AfUnixSocketPathAccess(#[from] fs_mistrust::Error),
#[error("Problem accessing socket address file")]
SocketAddressFileAccess(#[source] fs_mistrust::Error),
#[error("Invalid JSON contents in socket address file")]
SocketAddressFileJson(#[source] Arc<serde_json::Error>),
#[error("Invalid address in socket address file")]
SocketAddressFileContent(#[source] general::AddrParseError),
#[error("Socket address file contents didn't match connect point")]
SocketAddressFileMismatch,
#[error("Could not acquire lock: Another process is listening on this connect point")]
AlreadyLocked,
#[error("Internal error: {0}")]
Internal(String),
}
impl From<io::Error> for ConnectError {
fn from(err: io::Error) -> Self {
ConnectError::Io(Arc::new(err))
}
}
impl crate::HasClientErrorAction for ConnectError {
fn client_action(&self) -> crate::ClientErrorAction {
use crate::ClientErrorAction as A;
use ConnectError as E;
match self {
E::Io(err) => crate::net_error_action(err),
E::ExplicitAbort => A::Abort,
E::LoadCookie(err) => err.client_action(),
E::UnsupportedSocketType => A::Decline,
E::UnsupportedAuthType => A::Decline,
E::AfUnixSocketPathAccess(err) => err.client_action(),
E::SocketAddressFileAccess(err) => err.client_action(),
E::SocketAddressFileJson(_) => A::Decline,
E::SocketAddressFileContent(_) => A::Decline,
E::SocketAddressFileMismatch => A::Decline,
E::AlreadyLocked => A::Abort, E::Internal(_) => A::Abort,
}
}
}
#[cfg(any(feature = "rpc-client", feature = "rpc-server"))]
fn socket_parent_path(addr: &tor_general_addr::general::SocketAddr) -> Option<&std::path::Path> {
addr.as_pathname().and_then(|p| p.parent())
}
pub const USER_DEFAULT_CONNECT_POINT: &str = {
cfg_if::cfg_if! {
if #[cfg(unix)] {
r#"
[connect]
socket = "unix:${ARTI_LOCAL_DATA}/rpc/arti_rpc_socket"
auth = "none"
"#
} else {
r#"
[connect]
socket = "inet:127.0.0.1:9180"
auth = { cookie = { path = "${ARTI_LOCAL_DATA}/rpc/arti_rpc_cookie" } }
"#
}
}
};
pub const SYSTEM_DEFAULT_CONNECT_POINT: Option<&str> = {
cfg_if::cfg_if! {
if #[cfg(unix)] {
Some(
r#"
[connect]
socket = "unix:/var/run/arti-rpc/arti_rpc_socket"
auth = "none"
"#
)
} else {
None
}
}
};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum SuperuserPermission {
Allowed,
NotAllowed,
}
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use super::*;
#[test]
fn parse_defaults() {
let _parsed: ParsedConnectPoint = USER_DEFAULT_CONNECT_POINT.parse().unwrap();
if let Some(s) = SYSTEM_DEFAULT_CONNECT_POINT {
let _parsed: ParsedConnectPoint = s.parse().unwrap();
}
}
}