1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3#![allow(renamed_and_removed_lints)] #![allow(unknown_lints)] #![warn(missing_docs)]
7#![warn(noop_method_call)]
8#![warn(unreachable_pub)]
9#![warn(clippy::all)]
10#![deny(clippy::await_holding_lock)]
11#![deny(clippy::cargo_common_metadata)]
12#![deny(clippy::cast_lossless)]
13#![deny(clippy::checked_conversions)]
14#![warn(clippy::cognitive_complexity)]
15#![deny(clippy::debug_assert_with_mut_call)]
16#![deny(clippy::exhaustive_enums)]
17#![deny(clippy::exhaustive_structs)]
18#![deny(clippy::expl_impl_clone_on_copy)]
19#![deny(clippy::fallible_impl_from)]
20#![deny(clippy::implicit_clone)]
21#![deny(clippy::large_stack_arrays)]
22#![warn(clippy::manual_ok_or)]
23#![deny(clippy::missing_docs_in_private_items)]
24#![warn(clippy::needless_borrow)]
25#![warn(clippy::needless_pass_by_value)]
26#![warn(clippy::option_option)]
27#![deny(clippy::print_stderr)]
28#![deny(clippy::print_stdout)]
29#![warn(clippy::rc_buffer)]
30#![deny(clippy::ref_option_ref)]
31#![warn(clippy::semicolon_if_nothing_returned)]
32#![warn(clippy::trait_duplication_in_bounds)]
33#![deny(clippy::unchecked_time_subtraction)]
34#![deny(clippy::unnecessary_wraps)]
35#![warn(clippy::unseparated_literal_suffix)]
36#![deny(clippy::unwrap_used)]
37#![deny(clippy::mod_module_files)]
38#![allow(clippy::let_unit_value)] #![allow(clippy::uninlined_format_args)]
40#![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)]
47#![deny(clippy::string_slice)] #![cfg_attr(not(all(feature = "full")), allow(unused))]
52
53pub mod auth;
54#[cfg(feature = "rpc-client")]
55pub mod client;
56mod connpt;
57pub mod load;
58#[cfg(feature = "rpc-server")]
59pub mod server;
60#[cfg(test)]
61mod testing;
62
63use std::{io, sync::Arc};
64
65pub use connpt::{ParsedConnectPoint, ResolveError, ResolvedConnectPoint};
66use tor_general_addr::general;
67
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
72#[allow(clippy::exhaustive_enums)]
73pub enum ClientErrorAction {
74 Abort,
76 Decline,
78}
79pub trait HasClientErrorAction {
81 fn client_action(&self) -> ClientErrorAction;
83}
84impl HasClientErrorAction for tor_config_path::CfgPathError {
85 fn client_action(&self) -> ClientErrorAction {
86 ClientErrorAction::Abort
89 }
90}
91impl HasClientErrorAction for tor_config_path::addr::CfgAddrError {
92 fn client_action(&self) -> ClientErrorAction {
93 use ClientErrorAction as A;
94 use tor_config_path::addr::CfgAddrError as CAE;
95 match self {
96 CAE::NoAfUnixSocketSupport(_) => A::Decline,
97 CAE::Path(cfg_path_error) => cfg_path_error.client_action(),
98 CAE::ConstructAfUnixAddress(_) => A::Abort,
99 _ => A::Abort,
101 }
102 }
103}
104impl HasClientErrorAction for tor_general_addr::general::AddrParseError {
105 fn client_action(&self) -> ClientErrorAction {
106 use ClientErrorAction as A;
107 use tor_general_addr::general::AddrParseError as E;
108 match self {
109 E::UnrecognizedSchema(_) => A::Decline,
110 E::NoSchema => A::Decline,
111 E::InvalidAfUnixAddress(_) => A::Abort,
112 E::InvalidInetAddress(_) => A::Decline,
115 _ => A::Abort,
117 }
118 }
119}
120
121fn fs_error_action(err: &std::io::Error) -> ClientErrorAction {
127 use ClientErrorAction as A;
128 use std::io::ErrorKind as EK;
129 match err.kind() {
130 EK::NotFound => A::Decline,
131 EK::PermissionDenied => A::Decline,
132 EK::ReadOnlyFilesystem => A::Decline,
133 _ => A::Abort,
134 }
135}
136fn net_error_action(err: &std::io::Error) -> ClientErrorAction {
142 use ClientErrorAction as A;
143 use std::io::ErrorKind as EK;
144 match err.kind() {
145 EK::ConnectionRefused => A::Decline,
146 EK::ConnectionReset => A::Decline,
147 EK::HostUnreachable => A::Decline,
148 EK::NetworkDown => A::Decline,
149 EK::NetworkUnreachable => A::Decline,
150 _ => A::Abort,
151 }
152}
153impl HasClientErrorAction for fs_mistrust::Error {
154 fn client_action(&self) -> ClientErrorAction {
155 use ClientErrorAction as A;
156 use fs_mistrust::Error as E;
157 match self {
158 E::Multiple(errs) => {
159 if errs.iter().any(|e| e.client_action() == A::Abort) {
160 A::Abort
161 } else {
162 A::Decline
163 }
164 }
165 E::Io { err, .. } => fs_error_action(err),
166 E::CouldNotInspect(_, err) => fs_error_action(err),
167
168 E::NotFound(_) => A::Decline,
169 E::BadPermission(_, _, _) | E::BadOwner(_, _) => A::Decline,
170 E::StepsExceeded | E::CurrentDirectory(_) => A::Abort,
171
172 E::BadType(_) => A::Abort,
173
174 E::CreatingDir(_)
176 | E::Content(_)
177 | E::NoSuchGroup(_)
178 | E::NoSuchUser(_)
179 | E::MissingField(_)
180 | E::InvalidSubdirectory => A::Abort,
181 E::PasswdGroupIoError(_) => A::Abort,
182 _ => A::Abort,
183 }
184 }
185}
186
187#[derive(Clone, Debug, thiserror::Error)]
189#[non_exhaustive]
190pub enum ConnectError {
191 #[error("IO error while connecting")]
193 Io(#[source] Arc<io::Error>),
194 #[error("Encountered an explicit \"abort\"")]
196 ExplicitAbort,
197 #[error("Unable to load cookie file")]
199 LoadCookie(#[from] auth::cookie::CookieAccessError),
200 #[error("Unsupported socket type")]
202 UnsupportedSocketType,
203 #[error("Unsupported authentication type")]
205 UnsupportedAuthType,
206 #[error("Unix domain socket path access")]
208 AfUnixSocketPathAccess(#[from] fs_mistrust::Error),
209 #[error("Problem accessing socket address file")]
211 SocketAddressFileAccess(#[source] fs_mistrust::Error),
212 #[error("Invalid JSON contents in socket address file")]
214 SocketAddressFileJson(#[source] Arc<serde_json::Error>),
215 #[error("Invalid address in socket address file")]
217 SocketAddressFileContent(#[source] general::AddrParseError),
218 #[error("Socket address file contents didn't match connect point")]
220 SocketAddressFileMismatch,
221 #[error("Could not acquire lock: Another process is listening on this connect point")]
224 AlreadyLocked,
225 #[error("Internal error: {0}")]
229 Internal(String),
230}
231
232impl From<io::Error> for ConnectError {
233 fn from(err: io::Error) -> Self {
234 ConnectError::Io(Arc::new(err))
235 }
236}
237impl crate::HasClientErrorAction for ConnectError {
238 fn client_action(&self) -> crate::ClientErrorAction {
239 use crate::ClientErrorAction as A;
240 use ConnectError as E;
241 match self {
242 E::Io(err) => crate::net_error_action(err),
243 E::ExplicitAbort => A::Abort,
244 E::LoadCookie(err) => err.client_action(),
245 E::UnsupportedSocketType => A::Decline,
246 E::UnsupportedAuthType => A::Decline,
247 E::AfUnixSocketPathAccess(err) => err.client_action(),
248 E::SocketAddressFileAccess(err) => err.client_action(),
249 E::SocketAddressFileJson(_) => A::Decline,
250 E::SocketAddressFileContent(_) => A::Decline,
251 E::SocketAddressFileMismatch => A::Decline,
252 E::AlreadyLocked => A::Abort, E::Internal(_) => A::Abort,
254 }
255 }
256}
257#[cfg(any(feature = "rpc-client", feature = "rpc-server"))]
258fn socket_parent_path(addr: &tor_general_addr::general::SocketAddr) -> Option<&std::path::Path> {
260 addr.as_pathname().and_then(|p| p.parent())
261}
262
263pub const USER_DEFAULT_CONNECT_POINT: &str = {
265 cfg_if::cfg_if! {
266 if #[cfg(unix)] {
267r#"
268[connect]
269socket = "unix:${ARTI_LOCAL_DATA}/rpc/arti_rpc_socket"
270auth = "none"
271"#
272 } else {
273r#"
274[connect]
275socket = "inet:127.0.0.1:9180"
276auth = { cookie = { path = "${ARTI_LOCAL_DATA}/rpc/arti_rpc_cookie" } }
277"#
278 }
279 }
280};
281
282pub const SYSTEM_DEFAULT_CONNECT_POINT: Option<&str> = {
286 cfg_if::cfg_if! {
287 if #[cfg(unix)] {
288 Some(
289r#"
290[connect]
291socket = "unix:/var/run/arti-rpc/arti_rpc_socket"
292auth = "none"
293"#
294 )
295 } else {
296 None
297 }
298 }
299};
300
301#[derive(Copy, Clone, Debug, PartialEq, Eq)]
304#[non_exhaustive]
305pub enum SuperuserPermission {
306 Allowed,
308 NotAllowed,
310}
311
312#[cfg(test)]
313mod test {
314 #![allow(clippy::bool_assert_comparison)]
316 #![allow(clippy::clone_on_copy)]
317 #![allow(clippy::dbg_macro)]
318 #![allow(clippy::mixed_attributes_style)]
319 #![allow(clippy::print_stderr)]
320 #![allow(clippy::print_stdout)]
321 #![allow(clippy::single_char_pattern)]
322 #![allow(clippy::unwrap_used)]
323 #![allow(clippy::unchecked_time_subtraction)]
324 #![allow(clippy::useless_vec)]
325 #![allow(clippy::needless_pass_by_value)]
326 #![allow(clippy::string_slice)] use super::*;
330
331 #[test]
332 fn parse_defaults() {
333 let _parsed: ParsedConnectPoint = USER_DEFAULT_CONNECT_POINT.parse().unwrap();
334 if let Some(s) = SYSTEM_DEFAULT_CONNECT_POINT {
335 let _parsed: ParsedConnectPoint = s.parse().unwrap();
336 }
337 }
338}