1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
//! High-level functionality for accessing the Tor network as a client.
//!
//! # Overview
//!
//! The `arti-client` crate aims to provide a safe, easy-to-use API for
//! applications that want to use the Tor network to anonymize their traffic.
//!
//! This crate is part of [Arti](https://gitlab.torproject.org/tpo/core/arti/),
//! a project to implement [Tor](https://www.torproject.org/) in Rust. It is the
//! highest-level library crate in Arti, and the one that nearly all client-only
//! programs should use. Most of its functionality is provided by lower-level
//! crates in Arti.
//!
//! ## ⚠ Warnings ⚠
//!
//! Note that Arti is a work in progress; although we've tried to write all the
//! critical security components, you probably shouldn't use Arti in production
//! until it's a bit more mature. (That said, now is a _great_ time to try our
//! Arti on an experimental basis, so you can tell us what we need to fix
//! between now and the 1.0.0 release.)
//!
//! Also note that the APIs for this crate are not all yet completely stable.
//! We'll try not to break things without good reason, and we'll follow semantic
//! versioning when we do, but please expect a certain amount of breakage
//! between now and 1.0.0.
//!
//! The APIs exposed by lower-level crates in Arti are _even more unstable_;
//! they will break more often than those from `arti-client`, for less reason.
//!
//! # Using `arti-client`
//!
//! The main entry point for this crate is the [`TorClient`], an object that
//! lets you make connections over the Tor network.
//!
//! ## Connecting to Tor
//!
//! Calling [`TorClient::create_bootstrapped`] establishes a connection to the
//! Tor network, pulling in necessary state about network consensus as required.
//! This state gets persisted to the locations specified in the
//! [`TorClientConfig`].
//!
//! (This method requires you to initialize the client in an `async fn`.
//! Consider using the builder method, below, if that doesn't work for you.)
//!
//! ```no_run
//! # use anyhow::Result;
//! # use arti_client::{TorClient, TorClientConfig};
//! # use tokio_crate as tokio;
//! # #[tokio::main]
//! # async fn main() -> Result<()> {
//! // The client configuration describes how to connect to the Tor network,
//! // and what directories to use for storing persistent state.
//! let config = TorClientConfig::default();
//!
//! // Start the Arti client, and let it bootstrap a connection to the Tor network.
//! // (This takes a while to gather the necessary directory information.
//! // It uses cached information when possible.)
//! let tor_client = TorClient::create_bootstrapped(config).await?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Creating a client and connecting later
//!
//! You might wish to create a Tor client immediately, without waiting for it to
//! bootstrap (or having to use an `await`). This can be done by making a
//! [`TorClientBuilder`] with [`TorClient::builder`], and calling
//! [`TorClientBuilder::create_unbootstrapped`].
//!
//! The returned client can be made to bootstrap when it is first used (the
//! default), or not; see [`BootstrapBehavior`] for more details.
//!
//! ```no_run
//! # use anyhow::Result;
//! # use arti_client::{TorClient, TorClientConfig};
//! # use tokio_crate as tokio;
//! # use arti_client::BootstrapBehavior;
//! # #[tokio::main]
//! # async fn main() -> Result<()> {
//! // Specifying `BootstrapBehavior::OnDemand` means the client will automatically
//! // bootstrap when it is used. `Manual` exists if you'd rather have full control.
//! let tor_client = TorClient::builder()
//! .bootstrap_behavior(BootstrapBehavior::OnDemand)
//! .create_unbootstrapped()?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Using the client
//!
//! A client can then be used to make connections over Tor with
//! [`TorClient::connect`], which accepts anything implementing [`IntoTorAddr`].
//! This returns a [`DataStream`], an anonymized TCP stream type that implements
//! [`AsyncRead`](futures::io::AsyncRead) and
//! [`AsyncWrite`](futures::io::AsyncWrite), as well as the Tokio versions of
//! those traits if the `tokio` crate feature is enabled.
//!
//! ## Example: making connections over Tor
//!
//! ```no_run
//! # use anyhow::Result;
//! # use arti_client::{TorClient, TorClientConfig};
//! # use tokio_crate as tokio;
//! # #[tokio::main]
//! # async fn main() -> Result<()> {
//! # let config = TorClientConfig::default();
//! # let tor_client = TorClient::create_bootstrapped(config).await?;
//! #
//! // Initiate a connection over Tor to example.com, port 80.
//! let mut stream = tor_client.connect(("example.com", 80)).await?;
//!
//! use futures::io::{AsyncReadExt, AsyncWriteExt};
//!
//! // Write out an HTTP request.
//! stream
//! .write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n")
//! .await?;
//!
//! // IMPORTANT: Make sure the request was written.
//! // Arti buffers data, so flushing the buffer is usually required.
//! stream.flush().await?;
//!
//! // Read and print the result.
//! let mut buf = Vec::new();
//! stream.read_to_end(&mut buf).await?;
//!
//! println!("{}", String::from_utf8_lossy(&buf));
//! #
//! # Ok(())
//! # }
//! ```
//!
//! ## More advanced usage
//!
//! This version of Arti includes basic support for "stream isolation": the
//! ability to ensure that different TCP connections ('streams') go over
//! different Tor circuits (and thus different exit nodes, making them originate
//! from different IP addresses).
//!
//! This is useful to avoid deanonymizing users by correlation: for example, you
//! might want a Tor connection to your bank and a Tor connection to an online
//! forum to use different circuits, to avoid the possibility of the two
//! identities being linked by having the same source IP.
//!
//! Streams can be isolated in two ways:
//!
//! - by calling [`TorClient::isolated_client`], which returns a new
//! [`TorClient`] whose streams will use a different circuit
//! - by generating [`IsolationToken`]s, and passing them in via [`StreamPrefs`]
//! to [`TorClient::connect`].
//!
//! # Multiple runtime support
//!
//! Arti uses the [`tor_rtcompat`] crate to support multiple asynchronous
//! runtimes; currently, both [Tokio](https://tokio.rs) and
//! [async-std](https://async.rs) are supported.
//!
//! The backend Arti uses for TCP connections ([`tor_rtcompat::TcpProvider`])
//! and for creating TLS sessions ([`tor_rtcompat::TlsProvider`]) is also
//! configurable using this crate. This can be used to embed Arti in custom
//! environments where you want lots of control over how it uses the network.
//!
//! [**View the `tor_rtcompat` crate documentation**](tor_rtcompat) for more
//! about these features.
//!
//! # Feature flags
//!
//! ## Additive features
//!
//! * `tokio` (default) -- build with [Tokio](https://tokio.rs/) support
//! * `native-tls` (default) -- build with the
//! [native-tls](https://github.com/sfackler/rust-native-tls) crate for TLS
//! support
//! * `async-std` -- build with [async-std](https://async.rs/) support
//!
//! * `full` -- Build with all features above, along with all stable additive
//! features from other arti crates. (This does not include experimental
//! features. It also does not include features that select a particular
//! implementation to the exclusion of another, or those that set a build
//! flag.)
//!
//! * `rustls` -- build with the [rustls](https://github.com/rustls/rustls)
//! crate for TLS support. This is not included in `full`, since it uses the
//! `ring` crate, which uses the old (3BSD/SSLEay) OpenSSL license, which may
//! introduce licensing compatibility issues.
//!
//! Note that flags `tokio`, `native-tls`, `async-std`, `rustls` and `static`
//! will enable the flags of the same name on the [`tor_rtcompat`] crate.
//!
//! ## Build-flag related features
//!
//! * `static` -- link with static versions of Arti's system dependencies, like
//! SQLite and OpenSSL (⚠ Warning ⚠: this feature will include a dependency on
//! native-tls, even if you weren't planning to use native-tls. If you only
//! want to build with a static sqlite library, enable the `static-sqlite`
//! feature. We'll look for better solutions here in the future.)
//! * `static-sqlite` -- link with a static version of sqlite.
//! * `static-native-tls` -- link with a static version of `native-tls`. Enables
//! `native-tls`.
//!
//! ## Cryptographic acceleration features
//!
//! Libraries should not enable these by default, since they replace one
//! implementation with another.
//!
//! * `accel-sha1-asm` -- Accelerate cryptography by using an assembly
//! implementation of SHA1, if one is available.
//! * `accel-openssl` -- Accelerate cryptography by using openssl as a backend.
//!
//! ## Experimental and unstable features
//!
//! Note that the APIs enabled by these features are NOT covered by semantic
//! versioning guarantees: we might break them or remove them between patch
//! versions.
//!
//! * `experimental-api` -- build with experimental, unstable API support.
//! * `error_detail` -- expose the `arti_client::Error` inner error type.
//! * `dirfilter` -- expose the `DirFilter` API, which lets you modify a network
//! directory before it is used.
//!
//! * `experimental` -- Build with all experimental features above, along with
//! all experimental features from other arti crates.
// @@ begin lint list maintained by maint/add_warning @@
#![deny(missing_docs)]
#![warn(noop_method_call)]
#![deny(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)]
#![deny(clippy::missing_panics_doc)]
#![warn(clippy::needless_borrow)]
#![warn(clippy::needless_pass_by_value)]
#![warn(clippy::option_option)]
#![warn(clippy::rc_buffer)]
#![deny(clippy::ref_option_ref)]
#![warn(clippy::semicolon_if_nothing_returned)]
#![warn(clippy::trait_duplication_in_bounds)]
#![deny(clippy::unnecessary_wraps)]
#![warn(clippy::unseparated_literal_suffix)]
#![deny(clippy::unwrap_used)]
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
mod address;
mod builder;
mod client;
mod util;
pub mod config;
pub mod status;
pub use address::{DangerouslyIntoTorAddr, IntoTorAddr, TorAddr, TorAddrError};
pub use builder::TorClientBuilder;
pub use client::{BootstrapBehavior, DormantMode, StreamPrefs, TorClient};
pub use config::TorClientConfig;
pub use tor_circmgr::isolation;
pub use tor_circmgr::IsolationToken;
pub use tor_error::{ErrorKind, HasKind};
pub use tor_proto::stream::{DataReader, DataStream, DataWriter};
mod err;
pub use err::Error;
#[cfg(feature = "error_detail")]
pub use err::ErrorDetail;
/// Alias for the [`Result`] type corresponding to the high-level [`Error`].
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(feature = "experimental-api")]
pub use builder::DirProviderBuilder;