Merge remote-tracking branch 'origin/mr/111'

This commit is contained in:
Nick Mathewson 2021-10-28 19:54:03 -04:00
commit 19d3c34729
3 changed files with 146 additions and 16 deletions

View File

@ -0,0 +1,46 @@
use anyhow::Result;
use arti_client::{TorClient, TorClientConfig};
use tokio_crate as tokio;
use futures::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> Result<()> {
// Arti uses the `tracing` crate for logging. Install a handler for this, to print Arti's logs.
tracing_subscriber::fmt::init();
// The client config includes things like where to store persistent Tor network state.
// The "sane defaults" provided are the same as the Arti standalone application, and save data
// to a conventional place depending on operating system (for example, ~/.local/share/arti
// on Linux platforms)
let config = TorClientConfig::sane_defaults()?;
// Arti needs an async runtime handle to spawn async tasks.
let rt = tor_rtcompat::tokio::current_runtime()?;
eprintln!("connecting to Tor...");
// We now let the Arti client start and bootstrap a connection to the network.
// (This takes a while to gather the necessary consensus state, etc.)
let tor_client = TorClient::bootstrap(rt, config).await?;
eprintln!("connecting to example.com...");
let mut stream = tor_client.connect(("example.com", 80), None).await?;
eprintln!("sending request...");
stream
.write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n")
.await?;
stream.flush().await?;
eprintln!("reading response...");
let mut buf = Vec::new();
stream.read_to_end(&mut buf).await?;
println!("{}", String::from_utf8_lossy(&buf));
Ok(())
}

View File

@ -3,7 +3,7 @@
use thiserror::Error;
use tor_rtcompat::TimeoutError;
/// An error originating from the tor-dirclient crate.
/// Represents errors that can occur while doing Tor operations.
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum Error {

View File

@ -24,24 +24,108 @@
//! general, are not the least bit stable. If you use this code,
//! please expect your software to break on a regular basis.
//!
//! ## Design considerations, privacy considerations.
//!
//! As we build the APIs for this crate, we've been aiming for
//! simplicity and safety: we want it to be as easy as possible to use
//! `arti-client`, while trying to make certain kinds of privacy or security
//! violation hard to write accidentally.
//!
//! Privacy isn't just a drop-in feature, however. There are still
//! plenty of ways to accidentally leak information, even if you're
//! anonymizing your connections over Tor. We'll try to document
//! those in a user's guide at some point as Arti becomes more mature.
//!
//! # Using `arti-client`
//!
//! The `arti-client` crate provides an async Rust API. It is
//! compatible with the `tokio` and `async_std` asynchronous backends.
//! The main entry point for this crate is the [`TorClient`], an object that lets you make
//! connections over the Tor network.
//!
//! Calling [`TorClient::bootstrap`] 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`].
//!
//! A client can then be used to make connections over Tor with [`TorClient::connect`], which
//! accepts anything implementing [`IntoTorAddr`]. This returns a [`DataStream`], an anonymised
//! 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.
//!
//! The [`TorAddr`] type is intended to ensure that DNS lookups are done via the Tor network
//! instead of locally. Doing local DNS resolution can leak information about which hostnames you're
//! connecting to to your local DNS resolver (i.e. your ISP), so it's much better to let Arti
//! do it for you to maintain privacy.
//!
//! If you really want to connect to a raw IP address and know what you're doing, take a look at
//! [`TorAddr::dangerously_from`] -- but be careful!
//!
//! ## 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<()> {
//! // The client config includes things like where to store persistent Tor network state.
//! let config = TorClientConfig::sane_defaults()?;
//! // Arti needs a handle to an async runtime in order to spawn async tasks.
//! // (See "Multiple runtime support" below.)
//! let rt = tor_rtcompat::tokio::current_runtime()?;
//!
//! // Start the Arti client, and let it bootstrap a connection to the Tor network.
//! // (This takes a while to gather the necessary consensus state, etc.)
//! let tor_client = TorClient::bootstrap(rt, config).await?;
//!
//! // Initiate a connection over Tor to example.com, port 80.
//! let mut stream = tor_client.connect(("example.com", 80), None).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 by default due to the design of the Tor protocol, 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 deanonymising
//! 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 [`ConnectPrefs`] 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.
//!
//! Functions in this crate, like [`TorClient::bootstrap`],
//! will expect a type that implements [`tor_rtcompat::Runtime`], which can be obtained:
//!
//! - for Tokio:
//! - by calling [`tor_rtcompat::tokio::current_runtime`], if a Tokio reactor is already running
//! - by calling [`tor_rtcompat::tokio::create_runtime`], to start a new reactor if one is not
//! already running
//! - by manually creating a [`TokioRuntimeHandle`](tor_rtcompat::tokio::TokioRuntimeHandle) from
//! an existing Tokio runtime handle
//! - for async-std:
//! - by calling [`tor_rtcompat::async_std::current_runtime`], which will create a runtime or
//! retrieve the existing one, if one has already been started
//!
//! TODO: Good examples here once the crate setup API is more simple.
//!
//! # Feature flags
//!