1
1
//! High-level functionality for accessing the Tor network as a client.
2
//!
3
//! # Overview
4
//!
5
//! The `arti-client` crate aims to provide a safe, easy-to-use API for
6
//! applications that want to use the Tor network to anonymize their traffic.
7
//!
8
//! This crate is part of [Arti](https://gitlab.torproject.org/tpo/core/arti/),
9
//! a project to implement [Tor](https://www.torproject.org/) in Rust. It is the
10
//! highest-level library crate in Arti, and the one that nearly all client-only
11
//! programs should use. Most of its functionality is provided by lower-level
12
//! crates in Arti.
13
//!
14
//! ## ⚠ Warnings ⚠
15
//!
16
//! Note that Arti is a work in progress; although we've tried to write all the
17
//! critical security components, you probably shouldn't use Arti in production
18
//! until it's a bit more mature.  (That said, now is a _great_ time to try
19
//! our Arti on an experimental basis, so you can tell us what we need
20
//! to fix between now and the 1.0.0 release.)
21
//!
22
//! Also note that the APIs for this crate are not all yet
23
//! completely stable.  We'll try not to break things without good
24
//! reason, and we'll follow semantic versioning when we do, but
25
//! please expect a certain amount of breakage between now and 1.0.0.
26
//!
27
//! The APIs exposed by lower-level crates in Arti are _even more
28
//! unstable_; they will break more often than those from
29
//! `arti-client`, for less reason.
30
//!
31
//! # Using `arti-client`
32
//!
33
//! The main entry point for this crate is the [`TorClient`], an object that
34
//! lets you make connections over the Tor network.
35
//!
36
//! ## Connecting to Tor
37
//!
38
//! Calling [`TorClient::create_bootstrapped`] establishes a connection to the Tor
39
//! network, pulling in necessary state about network consensus as required.
40
//! This state gets persisted to the locations specified in the
41
//! [`TorClientConfig`].
42
//!
43
//! (This method requires you to initialize the client in an `async fn`. Consider
44
//! using the builder method, below, if that doesn't work for you.)
45
//!
46
//! ```no_run
47
//! # use anyhow::Result;
48
//! # use arti_client::{TorClient, TorClientConfig};
49
//! # use tokio_crate as tokio;
50
//! # #[tokio::main]
51
//! # async fn main() -> Result<()> {
52
//! // The client configuration describes how to connect to the Tor network,
53
//! // and what directories to use for storing persistent state.
54
//! let config = TorClientConfig::default();
55
//!
56
//! // Start the Arti client, and let it bootstrap a connection to the Tor network.
57
//! // (This takes a while to gather the necessary directory information.
58
//! // It uses cached information when possible.)
59
//! let tor_client = TorClient::create_bootstrapped(config).await?;
60
//! #    Ok(())
61
//! # }
62
//! ```
63
//!
64
//! ## Creating a client and connecting later
65
//!
66
//! You might wish to create a Tor client immediately, without waiting for it to bootstrap (or
67
//! having to use an `await`). This can be done by making a [`TorClientBuilder`] with
68
//! [`TorClient::builder`], and calling [`TorClientBuilder::create_unbootstrapped`].
69
//!
70
//! The returned client can be made to bootstrap when it is first used (the default), or not;
71
//! see [`BootstrapBehavior`] for more details.
72
//!
73
//! ```no_run
74
//! # use anyhow::Result;
75
//! # use arti_client::{TorClient, TorClientConfig};
76
//! # use tokio_crate as tokio;
77
//! # use arti_client::BootstrapBehavior;
78
//! # #[tokio::main]
79
//! # async fn main() -> Result<()> {
80
//! // Specifying `BootstrapBehavior::OnDemand` means the client will automatically
81
//! // bootstrap when it is used. `Manual` exists if you'd rather have full control.
82
//! let tor_client = TorClient::builder()
83
//!     .bootstrap_behavior(BootstrapBehavior::OnDemand)
84
//!     .create_unbootstrapped()?;
85
//! #    Ok(())
86
//! # }
87
//! ```
88
//!
89
//! ## Using the client
90
//!
91
//! A client can then be used to make connections over Tor with
92
//! [`TorClient::connect`], which accepts anything implementing [`IntoTorAddr`].
93
//! This returns a [`DataStream`], an anonymized TCP stream type that implements
94
//! [`AsyncRead`](futures::io::AsyncRead) and
95
//! [`AsyncWrite`](futures::io::AsyncWrite), as well as the Tokio versions of
96
//! those traits if the `tokio` crate feature is enabled.
97
//!
98
//! ## Example: making connections over Tor
99
//!
100
//! ```no_run
101
//! # use anyhow::Result;
102
//! # use arti_client::{TorClient, TorClientConfig};
103
//! # use tokio_crate as tokio;
104
//! # #[tokio::main]
105
//! # async fn main() -> Result<()> {
106
//! #     let config = TorClientConfig::default();
107
//! #     let tor_client = TorClient::create_bootstrapped(config).await?;
108
//! #
109
//! // Initiate a connection over Tor to example.com, port 80.
110
//! let mut stream = tor_client.connect(("example.com", 80)).await?;
111
//!
112
//! use futures::io::{AsyncReadExt, AsyncWriteExt};
113
//!
114
//! // Write out an HTTP request.
115
//! stream
116
//!     .write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n")
117
//!     .await?;
118
//!
119
//! // IMPORTANT: Make sure the request was written.
120
//! // Arti buffers data, so flushing the buffer is usually required.
121
//! stream.flush().await?;
122
//!
123
//! // Read and print the result.
124
//! let mut buf = Vec::new();
125
//! stream.read_to_end(&mut buf).await?;
126
//!
127
//! println!("{}", String::from_utf8_lossy(&buf));
128
//! #
129
//! #    Ok(())
130
//! # }
131
//! ```
132
//!
133
//! ## More advanced usage
134
//!
135
//! This version of Arti includes basic support for "stream isolation": the
136
//! ability to ensure that different TCP connections ('streams') go over
137
//! different Tor circuits (and thus different exit nodes, making them originate
138
//! from different IP addresses).
139
//!
140
//! This is useful to avoid deanonymizing users by correlation: for example, you
141
//! might want a Tor connection to your bank and a Tor connection to an online
142
//! forum to use different circuits, to avoid the possibility of the two
143
//! identities being linked by having the same source IP.
144
//!
145
//! Streams can be isolated in two ways:
146
//!
147
//! - by calling [`TorClient::isolated_client`], which returns a new
148
//!   [`TorClient`] whose streams will use a different circuit
149
//! - by generating [`IsolationToken`]s, and passing them in via [`StreamPrefs`]
150
//!   to [`TorClient::connect`].
151
//!
152
//! # Multiple runtime support
153
//!
154
//! Arti uses the [`tor_rtcompat`] crate to support multiple asynchronous
155
//! runtimes; currently, both [Tokio](https://tokio.rs) and
156
//! [async-std](https://async.rs) are supported.
157
//!
158
//! The backend Arti uses for TCP connections ([`tor_rtcompat::TcpProvider`]) and for
159
//! creating TLS sessions ([`tor_rtcompat::TlsProvider`]) is also configurable using
160
//! this crate. This can be used to embed Arti in custom environments where you want
161
//! lots of control over how it uses the network.
162
//!
163
//! [**View the `tor_rtcompat` crate documentation**](tor_rtcompat) for more about these features.
164
//!
165
//! # Feature flags
166
//!
167
//! * `tokio` (default) -- build with [Tokio](https://tokio.rs/) support
168
//! * `native-tls` (default) -- build with the [native-tls](https://github.com/sfackler/rust-native-tls)
169
//!   crate for TLS support
170
//! * `async-std` -- build with [async-std](https://async.rs/) support
171
//! * `rustls` -- build with the [rustls](https://github.com/rustls/rustls) crate for TLS support
172
//! * `static` -- link with static versions of Arti's system dependencies, like SQLite and
173
//!   OpenSSL (⚠ Warning ⚠: this feature will include a dependency on native-tls, even if you weren't
174
//!   planning to use native-tls.  If you only want to build with a static sqlite library, enable the
175
//!   `static-sqlite` feature.  We'll look for better solutions here in the future.)
176
//! * `static-sqlite` -- link with a static version of sqlite.
177
//! * `static-native-tls` -- link with a static version of `native-tls`. Enables `native-tls`.
178
//! * `experimental-api` -- build with experimental, unstable API support. Note
179
//!   that these APIs are NOT covered by semantic versioning guarantees: we might
180
//!   break them or remove them between patch versions.
181
//! * `error_detail` -- expose the `arti_client::Error` inner error type. Note
182
//!   that this API is NOT covered by semantic versioning guarantees: we might
183
//!   break it between patch versions.
184
//!
185
//! Note that flags `tokio`, `native-tls`, `async-std`, `rustls` and `static` will enable
186
//! the flags of the same name on the [`tor_rtcompat`] crate.
187

            
188
#![deny(missing_docs)]
189
#![warn(noop_method_call)]
190
#![deny(unreachable_pub)]
191
#![warn(clippy::all)]
192
#![deny(clippy::await_holding_lock)]
193
#![deny(clippy::cargo_common_metadata)]
194
#![deny(clippy::cast_lossless)]
195
#![deny(clippy::checked_conversions)]
196
#![warn(clippy::cognitive_complexity)]
197
#![deny(clippy::debug_assert_with_mut_call)]
198
#![deny(clippy::exhaustive_enums)]
199
#![deny(clippy::exhaustive_structs)]
200
#![deny(clippy::expl_impl_clone_on_copy)]
201
#![deny(clippy::fallible_impl_from)]
202
#![deny(clippy::implicit_clone)]
203
#![deny(clippy::large_stack_arrays)]
204
#![warn(clippy::manual_ok_or)]
205
#![deny(clippy::missing_docs_in_private_items)]
206
#![deny(clippy::missing_panics_doc)]
207
#![warn(clippy::needless_borrow)]
208
#![warn(clippy::needless_pass_by_value)]
209
#![warn(clippy::option_option)]
210
#![warn(clippy::rc_buffer)]
211
#![deny(clippy::ref_option_ref)]
212
#![warn(clippy::semicolon_if_nothing_returned)]
213
#![warn(clippy::trait_duplication_in_bounds)]
214
#![deny(clippy::unnecessary_wraps)]
215
#![warn(clippy::unseparated_literal_suffix)]
216
#![deny(clippy::unwrap_used)]
217

            
218
mod address;
219
mod builder;
220
mod client;
221
mod util;
222

            
223
pub mod config;
224
pub mod status;
225

            
226
pub use address::{DangerouslyIntoTorAddr, IntoTorAddr, TorAddr, TorAddrError};
227
pub use builder::TorClientBuilder;
228
pub use client::{BootstrapBehavior, StreamPrefs, TorClient};
229
pub use config::TorClientConfig;
230

            
231
pub use tor_circmgr::IsolationToken;
232
pub use tor_error::{ErrorKind, HasKind};
233
pub use tor_proto::stream::{DataReader, DataStream, DataWriter};
234

            
235
mod err;
236
pub use err::Error;
237

            
238
#[cfg(feature = "error_detail")]
239
pub use err::ErrorDetail;
240

            
241
/// Alias for the [`Result`] type corresponding to the high-level [`Error`].
242
pub type Result<T> = std::result::Result<T, Error>;
243

            
244
#[cfg(feature = "experimental-api")]
245
pub use builder::DirProviderBuilder;