Loading Cargo.lock +200 −7 Original line number Diff line number Diff line Loading @@ -41,6 +41,15 @@ dependencies = [ "version_check", ] [[package]] name = "aho-corasick" version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] [[package]] name = "ansi_term" version = "0.12.1" Loading Loading @@ -166,6 +175,8 @@ dependencies = [ "hyper", "pin-project", "thiserror", "tls-api", "tls-api-native-tls", "tokio", "tor-error", "tor-rtcompat", Loading Loading @@ -323,7 +334,7 @@ checksum = "9c86f33abd5a4f3e2d6d9251a9e0c6a7e52eb1113caf893dae8429bf4a53f378" dependencies = [ "futures-lite", "rustls", "webpki", "webpki 0.21.4", ] [[package]] Loading Loading @@ -971,6 +982,19 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "env_logger" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" dependencies = [ "atty", "humantime 1.3.0", "log", "regex", "termcolor", ] [[package]] name = "event-listener" version = "2.5.2" Loading Loading @@ -1103,6 +1127,12 @@ dependencies = [ "winapi 0.3.9", ] [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "fuchsia-zircon" version = "0.3.3" Loading Loading @@ -1387,6 +1417,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "humantime" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ "quick-error", ] [[package]] name = "humantime" version = "2.1.0" Loading @@ -1399,7 +1438,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac34a56cfd4acddb469cc7fff187ed5ac36f498ba085caf8bbc725e3ff474058" dependencies = [ "humantime", "humantime 2.1.0", "serde", ] Loading Loading @@ -1997,6 +2036,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "pem" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" dependencies = [ "base64", "once_cell", "regex", ] [[package]] name = "pem-rfc7468" version = "0.2.4" Loading Loading @@ -2166,6 +2216,12 @@ dependencies = [ "unicode-xid", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quickcheck" version = "1.0.3" Loading @@ -2184,6 +2240,19 @@ dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" dependencies = [ "fuchsia-cprng", "libc", "rand_core 0.3.1", "rdrand", "winapi 0.3.9", ] [[package]] name = "rand" version = "0.7.3" Loading Loading @@ -2228,6 +2297,21 @@ dependencies = [ "rand_core 0.6.3", ] [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ "rand_core 0.4.2", ] [[package]] name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" version = "0.5.1" Loading Loading @@ -2255,6 +2339,15 @@ dependencies = [ "rand_core 0.5.1", ] [[package]] name = "rdrand" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" dependencies = [ "rand_core 0.3.1", ] [[package]] name = "redox_syscall" version = "0.2.10" Loading @@ -2280,6 +2373,8 @@ version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] Loading Loading @@ -2330,7 +2425,7 @@ dependencies = [ "libc", "once_cell", "spin", "untrusted", "untrusted 0.7.1", "web-sys", "winapi 0.3.9", ] Loading Loading @@ -2405,7 +2500,7 @@ dependencies = [ "log", "ring", "sct", "webpki", "webpki 0.21.4", ] [[package]] Loading Loading @@ -2456,7 +2551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" dependencies = [ "ring", "untrusted", "untrusted 0.7.1", ] [[package]] Loading Loading @@ -2718,6 +2813,16 @@ dependencies = [ "unicode-xid", ] [[package]] name = "tempdir" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" dependencies = [ "rand 0.4.6", "remove_dir_all", ] [[package]] name = "tempfile" version = "3.3.0" Loading @@ -2732,6 +2837,25 @@ dependencies = [ "winapi 0.3.9", ] [[package]] name = "termcolor" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" dependencies = [ "winapi-util", ] [[package]] name = "test-cert-gen" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3208d0ae2e3736d4ac2f6ba2229c4d9bbd54080e228e662a7684eabcf13ff419" dependencies = [ "pem", "tempdir", ] [[package]] name = "textwrap" version = "0.11.0" Loading Loading @@ -2804,6 +2928,53 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tls-api" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7dded74ddc6d4a98f9f94f17f1c4d796e4af3cb5fba9e7655f157a036ee7de0" dependencies = [ "anyhow", "log", "pem", "tempdir", "thiserror", "tokio", "void", "webpki 0.22.0", ] [[package]] name = "tls-api-native-tls" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c547db405b51a4e549f803c980572f3cb3957dff153b04e3e7aebb1fc5f249b4" dependencies = [ "anyhow", "native-tls", "thiserror", "tls-api", "tls-api-test", "tokio", ] [[package]] name = "tls-api-test" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "344ab291be7ed9ab296fc28153fe3ac1e430f44c4dfb3f1324a3c09bbbb5f104" dependencies = [ "anyhow", "env_logger", "log", "pem", "test-cert-gen", "tls-api", "tokio", "untrusted 0.6.2", "webpki 0.22.0", ] [[package]] name = "tokio" version = "1.17.0" Loading Loading @@ -3480,6 +3651,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "untrusted" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" [[package]] name = "untrusted" version = "0.7.1" Loading Loading @@ -3532,6 +3709,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "waker-fn" version = "1.1.0" Loading Loading @@ -3666,7 +3849,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ "ring", "untrusted", "untrusted 0.7.1", ] [[package]] name = "webpki" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" dependencies = [ "ring", "untrusted 0.7.1", ] [[package]] Loading Loading @@ -3792,7 +3985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb2bc2a902d992cd5f471ee3ab0ffd6603047a4207384562755b9d6de977518" dependencies = [ "ring", "untrusted", "untrusted 0.7.1", ] [[package]] Loading crates/arti-hyper/Cargo.toml +3 −1 Original line number Diff line number Diff line Loading @@ -23,14 +23,16 @@ static = [ "arti-client/static" ] experimental-api = [] [dependencies] anyhow = "1.0.23" arti-client = { path="../arti-client", version = "0.0.4"} hyper = { version = "0.14", features = ["http1", "client", "runtime"] } pin-project = "1" tokio = { package = "tokio", version = "1.7", features = ["rt", "rt-multi-thread", "io-util", "net", "time", "macros" ] } thiserror = "1" tls-api = { version = "0.7" } tls-api-native-tls = { version = "0.7.0" } tor-error = { path="../tor-error", version = "0.0.1" } tor-rtcompat = { path="../tor-rtcompat", version = "0.0.4", features=["tokio"] } [dev-dependencies] anyhow = "1.0.23" tracing-subscriber = "0.3.0" crates/arti-hyper/README.md +2 −4 Original line number Diff line number Diff line Loading @@ -8,9 +8,7 @@ High-level layer for making http(s) requests the Tor network as a client. Note that these APIs are NOT covered by semantic versioning guarantees: we might break them or remove them between patch versions. `error_detail` -- Make the `TorError` type transparent, and expose the `Error` within. Note that the resulting APIs are not stable. `native-tls` (default), `rustls` -- Select TLS libraries to support. `native-tls` (default), `rustls` -- Select TLS libraries to use for Tor's purposes. (The end-to-end TLS to the origin server is separate, and handled via `tls-api`.) License: MIT OR Apache-2.0 crates/arti-hyper/examples/hyper.rs +4 −2 Original line number Diff line number Diff line /// TODO this ought to support https! use arti_hyper::*; use anyhow::Result; use arti_client::{TorClient, TorClientConfig}; use hyper::Body; use std::convert::TryInto; use tls_api::{TlsConnector, TlsConnectorBuilder}; use tor_rtcompat::tokio::TokioNativeTlsRuntime; #[tokio::main] Loading Loading @@ -34,8 +34,10 @@ async fn main() -> Result<()> { // (This takes a while to gather the necessary consensus state, etc.) let tor_client = TorClient::create_bootstrapped(rt, config).await?; let tls_connector = tls_api_native_tls::TlsConnector::builder()?.build()?; // The `ArtiHttpConnector` lets us make HTTP requests via the Tor network. let tor_connector = ArtiHttpConnector::new(tor_client); let tor_connector = ArtiHttpConnector::new(tor_client, tls_connector); let http = hyper::Client::builder().build::<_, Body>(tor_connector); // The rest is just standard usage of Hyper. Loading crates/arti-hyper/src/lib.rs +107 −31 Original line number Diff line number Diff line //! High-level layer for making http(s) requests the Tor network as a client. //! //! Work-in-progress. //! This is **not suitable for use** right now because it does not support HTTPs. #![deny(missing_docs)] #![warn(noop_method_call)] Loading @@ -11,7 +8,6 @@ #![deny(clippy::cargo_common_metadata)] #![deny(clippy::cast_lossless)] #![deny(clippy::checked_conversions)] #![warn(clippy::clone_on_ref_ptr)] #![warn(clippy::cognitive_complexity)] #![deny(clippy::debug_assert_with_mut_call)] #![deny(clippy::exhaustive_enums)] Loading @@ -37,6 +33,7 @@ use std::future::Future; use std::io::Error; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use arti_client::{DataStream, IntoTorAddr, TorClient}; Loading @@ -46,6 +43,7 @@ use hyper::http::Uri; use hyper::service::Service; use pin_project::pin_project; use thiserror::Error; use tls_api::TlsConnector as TlsConn; // This is different from tor_rtompat::TlsConnector use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tor_rtcompat::Runtime; Loading @@ -62,7 +60,7 @@ pub enum ConnectionError { uri: Uri, }, /// Unsupported URI scheme /// Missing hostname #[error("Missing hostname in {uri:?}")] MissingHostname { /// URI Loading @@ -72,6 +70,10 @@ pub enum ConnectionError { /// Tor connection failed #[error("Tor connection failed")] Arti(#[from] arti_client::Error), /// TLS connection failed #[error("TLS connection failed")] TLS(#[source] Arc<anyhow::Error>), } /// We implement this for form's sake Loading @@ -84,6 +86,7 @@ impl tor_error::HasKind for ConnectionError { CE::UnsupportedUriScheme{..} => EK::NotImplemented, CE::MissingHostname{..} => EK::BadApiUsage, CE::Arti(e) => e.kind(), CE::TLS(_) => EK::RemoteProtocolFailed, } } } Loading @@ -91,29 +94,56 @@ impl tor_error::HasKind for ConnectionError { /// A `hyper` connector to proxy HTTP connections via the Tor network, using Arti. /// /// Only supports plaintext HTTP for now. #[derive(Clone)] pub struct ArtiHttpConnector<R: Runtime> { /// /// TC is the TLS to used *across* Tor to connect to the origin server. /// This is a different Rust type to the TLS used *by* Tor to connect to relays etc. /// It might even be a different underlying TLS implementation /// (although that is usually not a particularly good idea). pub struct ArtiHttpConnector<R: Runtime, TC: TlsConn> { /// The client client: TorClient<R>, /// TLS for using across Tor. tls_conn: Arc<TC>, } // #[derive(Clone)] infers a TC: Clone bound impl<R: Runtime, TC: TlsConn> Clone for ArtiHttpConnector<R, TC> { fn clone(&self) -> Self { let client = self.client.clone(); let tls_conn = self.tls_conn.clone(); Self { client, tls_conn } } } impl<R: Runtime> ArtiHttpConnector<R> { impl<R: Runtime, TC: TlsConn> ArtiHttpConnector<R, TC> { /// Make a new `ArtiHttpConnector` using an Arti `TorClient` object. pub fn new(client: TorClient<R>) -> Self { Self { client } pub fn new(client: TorClient<R>, tls_conn: TC) -> Self { let tls_conn = tls_conn.into(); Self { client, tls_conn } } } /// Wrapper type that makes an Arti `DataStream` implement necessary traits to be used as /// a `hyper` connection object (mainly `Connection`). #[pin_project] pub struct ArtiHttpConnection { pub struct ArtiHttpConnection<TC: TlsConn> { /// The stream #[pin] inner: DataStream, inner: MaybeHttpsStream<TC>, } /// The actual actual stream; might be TLS, might not #[pin_project(project = MaybeHttpsStreamProj)] enum MaybeHttpsStream<TC: TlsConn> { /// http Http(Pin<Box<DataStream>>), // Tc:TlsStream is generally boxed; box this one too /// https Https(#[pin] TC::TlsStream), } impl Connection for ArtiHttpConnection { impl<TC: TlsConn> Connection for ArtiHttpConnection<TC> { fn connected(&self) -> Connected { Connected::new() } Loading @@ -121,50 +151,83 @@ impl Connection for ArtiHttpConnection { // These trait implementations just defer to the inner `DataStream`; the wrapper type is just // there to implement the `Connection` trait. impl AsyncRead for ArtiHttpConnection { impl<TC: TlsConn> AsyncRead for ArtiHttpConnection<TC> { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<Result<(), std::io::Error>> { self.project().inner.poll_read(cx, buf) match self.project().inner.project() { MaybeHttpsStreamProj::Http(ds) => ds.as_mut().poll_read(cx, buf), MaybeHttpsStreamProj::Https(t) => t.poll_read(cx, buf), } } } impl AsyncWrite for ArtiHttpConnection { impl<TC: TlsConn> AsyncWrite for ArtiHttpConnection<TC> { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll<Result<usize, Error>> { self.project().inner.poll_write(cx, buf) match self.project().inner.project() { MaybeHttpsStreamProj::Http(ds) => ds.as_mut().poll_write(cx, buf), MaybeHttpsStreamProj::Https(t) => t.poll_write(cx, buf), } } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Error>> { self.project().inner.poll_flush(cx) match self.project().inner.project() { MaybeHttpsStreamProj::Http(ds) => ds.as_mut().poll_flush(cx), MaybeHttpsStreamProj::Https(t) => t.poll_flush(cx), } } fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Error>> { self.project().inner.poll_shutdown(cx) match self.project().inner.project() { MaybeHttpsStreamProj::Http(ds) => ds.as_mut().poll_shutdown(cx), MaybeHttpsStreamProj::Https(t) => t.poll_shutdown(cx), } } } /// Convert uri to host and port fn uri_to_host_port(uri: Uri) -> Result<(String, u16), ConnectionError> { if uri.scheme() != Some(&Scheme::HTTP) { #[derive(Debug, Clone, Copy, Eq, PartialEq)] /// Are we doing TLS? enum UseTls { /// No Bare, /// Yes Tls, } /// Convert uri to http[s] host and port, and whether to do tls fn uri_to_host_port_tls(uri: Uri) -> Result<(String, u16, UseTls), ConnectionError> { let use_tls = { // Scheme doesn't derive PartialEq so can't be matched on let scheme = uri.scheme(); if scheme == Some(&Scheme::HTTP) { UseTls::Bare } else if scheme == Some(&Scheme::HTTPS) { UseTls::Tls } else { return Err(ConnectionError::UnsupportedUriScheme { uri }); } }; let host = match uri.host() { Some(h) => h, _ => return Err(ConnectionError::MissingHostname { uri }), }; let port = uri.port().map(|x| x.as_u16()).unwrap_or(80); let port = uri.port().map(|x| x.as_u16()).unwrap_or(match use_tls { UseTls::Tls => 443, UseTls::Bare => 80, }); Ok((host.to_owned(), port)) Ok((host.to_owned(), port, use_tls)) } impl<R: Runtime> Service<Uri> for ArtiHttpConnector<R> { type Response = ArtiHttpConnection; impl<R: Runtime, TC: TlsConn> Service<Uri> for ArtiHttpConnector<R, TC> { type Response = ArtiHttpConnection<TC>; type Error = ConnectionError; #[allow(clippy::type_complexity)] type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>; Loading @@ -178,15 +241,28 @@ impl<R: Runtime> Service<Uri> for ArtiHttpConnector<R> { // underlying handles required to make Tor connections internally). // We use this to avoid the returned future having to borrow `self`. let client = self.client.clone(); let tls_conn = self.tls_conn.clone(); Box::pin(async move { // Extract the host and port to connect to from the URI. let (host, port) = uri_to_host_port(req)?; let (host, port, use_tls) = uri_to_host_port_tls(req)?; // Initiate a new Tor connection, producing a `DataStream` if successful. let addr = (&host as &str, port) .into_tor_addr() .map_err(arti_client::Error::from)?; let ds = client.connect(addr).await?; Ok(ArtiHttpConnection { inner: ds }) let inner = match use_tls { UseTls::Tls => { let conn = tls_conn .connect_impl_tls_stream(&host, ds) .await .map_err(|e| ConnectionError::TLS(e.into()))?; MaybeHttpsStream::Https(conn) } UseTls::Bare => MaybeHttpsStream::Http(Box::new(ds).into()), }; Ok(ArtiHttpConnection { inner }) }) } } Loading
Cargo.lock +200 −7 Original line number Diff line number Diff line Loading @@ -41,6 +41,15 @@ dependencies = [ "version_check", ] [[package]] name = "aho-corasick" version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] [[package]] name = "ansi_term" version = "0.12.1" Loading Loading @@ -166,6 +175,8 @@ dependencies = [ "hyper", "pin-project", "thiserror", "tls-api", "tls-api-native-tls", "tokio", "tor-error", "tor-rtcompat", Loading Loading @@ -323,7 +334,7 @@ checksum = "9c86f33abd5a4f3e2d6d9251a9e0c6a7e52eb1113caf893dae8429bf4a53f378" dependencies = [ "futures-lite", "rustls", "webpki", "webpki 0.21.4", ] [[package]] Loading Loading @@ -971,6 +982,19 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "env_logger" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" dependencies = [ "atty", "humantime 1.3.0", "log", "regex", "termcolor", ] [[package]] name = "event-listener" version = "2.5.2" Loading Loading @@ -1103,6 +1127,12 @@ dependencies = [ "winapi 0.3.9", ] [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "fuchsia-zircon" version = "0.3.3" Loading Loading @@ -1387,6 +1417,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "humantime" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ "quick-error", ] [[package]] name = "humantime" version = "2.1.0" Loading @@ -1399,7 +1438,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac34a56cfd4acddb469cc7fff187ed5ac36f498ba085caf8bbc725e3ff474058" dependencies = [ "humantime", "humantime 2.1.0", "serde", ] Loading Loading @@ -1997,6 +2036,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "pem" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" dependencies = [ "base64", "once_cell", "regex", ] [[package]] name = "pem-rfc7468" version = "0.2.4" Loading Loading @@ -2166,6 +2216,12 @@ dependencies = [ "unicode-xid", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quickcheck" version = "1.0.3" Loading @@ -2184,6 +2240,19 @@ dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" dependencies = [ "fuchsia-cprng", "libc", "rand_core 0.3.1", "rdrand", "winapi 0.3.9", ] [[package]] name = "rand" version = "0.7.3" Loading Loading @@ -2228,6 +2297,21 @@ dependencies = [ "rand_core 0.6.3", ] [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ "rand_core 0.4.2", ] [[package]] name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" version = "0.5.1" Loading Loading @@ -2255,6 +2339,15 @@ dependencies = [ "rand_core 0.5.1", ] [[package]] name = "rdrand" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" dependencies = [ "rand_core 0.3.1", ] [[package]] name = "redox_syscall" version = "0.2.10" Loading @@ -2280,6 +2373,8 @@ version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] Loading Loading @@ -2330,7 +2425,7 @@ dependencies = [ "libc", "once_cell", "spin", "untrusted", "untrusted 0.7.1", "web-sys", "winapi 0.3.9", ] Loading Loading @@ -2405,7 +2500,7 @@ dependencies = [ "log", "ring", "sct", "webpki", "webpki 0.21.4", ] [[package]] Loading Loading @@ -2456,7 +2551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" dependencies = [ "ring", "untrusted", "untrusted 0.7.1", ] [[package]] Loading Loading @@ -2718,6 +2813,16 @@ dependencies = [ "unicode-xid", ] [[package]] name = "tempdir" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" dependencies = [ "rand 0.4.6", "remove_dir_all", ] [[package]] name = "tempfile" version = "3.3.0" Loading @@ -2732,6 +2837,25 @@ dependencies = [ "winapi 0.3.9", ] [[package]] name = "termcolor" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" dependencies = [ "winapi-util", ] [[package]] name = "test-cert-gen" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3208d0ae2e3736d4ac2f6ba2229c4d9bbd54080e228e662a7684eabcf13ff419" dependencies = [ "pem", "tempdir", ] [[package]] name = "textwrap" version = "0.11.0" Loading Loading @@ -2804,6 +2928,53 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tls-api" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7dded74ddc6d4a98f9f94f17f1c4d796e4af3cb5fba9e7655f157a036ee7de0" dependencies = [ "anyhow", "log", "pem", "tempdir", "thiserror", "tokio", "void", "webpki 0.22.0", ] [[package]] name = "tls-api-native-tls" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c547db405b51a4e549f803c980572f3cb3957dff153b04e3e7aebb1fc5f249b4" dependencies = [ "anyhow", "native-tls", "thiserror", "tls-api", "tls-api-test", "tokio", ] [[package]] name = "tls-api-test" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "344ab291be7ed9ab296fc28153fe3ac1e430f44c4dfb3f1324a3c09bbbb5f104" dependencies = [ "anyhow", "env_logger", "log", "pem", "test-cert-gen", "tls-api", "tokio", "untrusted 0.6.2", "webpki 0.22.0", ] [[package]] name = "tokio" version = "1.17.0" Loading Loading @@ -3480,6 +3651,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "untrusted" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" [[package]] name = "untrusted" version = "0.7.1" Loading Loading @@ -3532,6 +3709,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "waker-fn" version = "1.1.0" Loading Loading @@ -3666,7 +3849,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ "ring", "untrusted", "untrusted 0.7.1", ] [[package]] name = "webpki" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" dependencies = [ "ring", "untrusted 0.7.1", ] [[package]] Loading Loading @@ -3792,7 +3985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb2bc2a902d992cd5f471ee3ab0ffd6603047a4207384562755b9d6de977518" dependencies = [ "ring", "untrusted", "untrusted 0.7.1", ] [[package]] Loading
crates/arti-hyper/Cargo.toml +3 −1 Original line number Diff line number Diff line Loading @@ -23,14 +23,16 @@ static = [ "arti-client/static" ] experimental-api = [] [dependencies] anyhow = "1.0.23" arti-client = { path="../arti-client", version = "0.0.4"} hyper = { version = "0.14", features = ["http1", "client", "runtime"] } pin-project = "1" tokio = { package = "tokio", version = "1.7", features = ["rt", "rt-multi-thread", "io-util", "net", "time", "macros" ] } thiserror = "1" tls-api = { version = "0.7" } tls-api-native-tls = { version = "0.7.0" } tor-error = { path="../tor-error", version = "0.0.1" } tor-rtcompat = { path="../tor-rtcompat", version = "0.0.4", features=["tokio"] } [dev-dependencies] anyhow = "1.0.23" tracing-subscriber = "0.3.0"
crates/arti-hyper/README.md +2 −4 Original line number Diff line number Diff line Loading @@ -8,9 +8,7 @@ High-level layer for making http(s) requests the Tor network as a client. Note that these APIs are NOT covered by semantic versioning guarantees: we might break them or remove them between patch versions. `error_detail` -- Make the `TorError` type transparent, and expose the `Error` within. Note that the resulting APIs are not stable. `native-tls` (default), `rustls` -- Select TLS libraries to support. `native-tls` (default), `rustls` -- Select TLS libraries to use for Tor's purposes. (The end-to-end TLS to the origin server is separate, and handled via `tls-api`.) License: MIT OR Apache-2.0
crates/arti-hyper/examples/hyper.rs +4 −2 Original line number Diff line number Diff line /// TODO this ought to support https! use arti_hyper::*; use anyhow::Result; use arti_client::{TorClient, TorClientConfig}; use hyper::Body; use std::convert::TryInto; use tls_api::{TlsConnector, TlsConnectorBuilder}; use tor_rtcompat::tokio::TokioNativeTlsRuntime; #[tokio::main] Loading Loading @@ -34,8 +34,10 @@ async fn main() -> Result<()> { // (This takes a while to gather the necessary consensus state, etc.) let tor_client = TorClient::create_bootstrapped(rt, config).await?; let tls_connector = tls_api_native_tls::TlsConnector::builder()?.build()?; // The `ArtiHttpConnector` lets us make HTTP requests via the Tor network. let tor_connector = ArtiHttpConnector::new(tor_client); let tor_connector = ArtiHttpConnector::new(tor_client, tls_connector); let http = hyper::Client::builder().build::<_, Body>(tor_connector); // The rest is just standard usage of Hyper. Loading
crates/arti-hyper/src/lib.rs +107 −31 Original line number Diff line number Diff line //! High-level layer for making http(s) requests the Tor network as a client. //! //! Work-in-progress. //! This is **not suitable for use** right now because it does not support HTTPs. #![deny(missing_docs)] #![warn(noop_method_call)] Loading @@ -11,7 +8,6 @@ #![deny(clippy::cargo_common_metadata)] #![deny(clippy::cast_lossless)] #![deny(clippy::checked_conversions)] #![warn(clippy::clone_on_ref_ptr)] #![warn(clippy::cognitive_complexity)] #![deny(clippy::debug_assert_with_mut_call)] #![deny(clippy::exhaustive_enums)] Loading @@ -37,6 +33,7 @@ use std::future::Future; use std::io::Error; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use arti_client::{DataStream, IntoTorAddr, TorClient}; Loading @@ -46,6 +43,7 @@ use hyper::http::Uri; use hyper::service::Service; use pin_project::pin_project; use thiserror::Error; use tls_api::TlsConnector as TlsConn; // This is different from tor_rtompat::TlsConnector use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tor_rtcompat::Runtime; Loading @@ -62,7 +60,7 @@ pub enum ConnectionError { uri: Uri, }, /// Unsupported URI scheme /// Missing hostname #[error("Missing hostname in {uri:?}")] MissingHostname { /// URI Loading @@ -72,6 +70,10 @@ pub enum ConnectionError { /// Tor connection failed #[error("Tor connection failed")] Arti(#[from] arti_client::Error), /// TLS connection failed #[error("TLS connection failed")] TLS(#[source] Arc<anyhow::Error>), } /// We implement this for form's sake Loading @@ -84,6 +86,7 @@ impl tor_error::HasKind for ConnectionError { CE::UnsupportedUriScheme{..} => EK::NotImplemented, CE::MissingHostname{..} => EK::BadApiUsage, CE::Arti(e) => e.kind(), CE::TLS(_) => EK::RemoteProtocolFailed, } } } Loading @@ -91,29 +94,56 @@ impl tor_error::HasKind for ConnectionError { /// A `hyper` connector to proxy HTTP connections via the Tor network, using Arti. /// /// Only supports plaintext HTTP for now. #[derive(Clone)] pub struct ArtiHttpConnector<R: Runtime> { /// /// TC is the TLS to used *across* Tor to connect to the origin server. /// This is a different Rust type to the TLS used *by* Tor to connect to relays etc. /// It might even be a different underlying TLS implementation /// (although that is usually not a particularly good idea). pub struct ArtiHttpConnector<R: Runtime, TC: TlsConn> { /// The client client: TorClient<R>, /// TLS for using across Tor. tls_conn: Arc<TC>, } // #[derive(Clone)] infers a TC: Clone bound impl<R: Runtime, TC: TlsConn> Clone for ArtiHttpConnector<R, TC> { fn clone(&self) -> Self { let client = self.client.clone(); let tls_conn = self.tls_conn.clone(); Self { client, tls_conn } } } impl<R: Runtime> ArtiHttpConnector<R> { impl<R: Runtime, TC: TlsConn> ArtiHttpConnector<R, TC> { /// Make a new `ArtiHttpConnector` using an Arti `TorClient` object. pub fn new(client: TorClient<R>) -> Self { Self { client } pub fn new(client: TorClient<R>, tls_conn: TC) -> Self { let tls_conn = tls_conn.into(); Self { client, tls_conn } } } /// Wrapper type that makes an Arti `DataStream` implement necessary traits to be used as /// a `hyper` connection object (mainly `Connection`). #[pin_project] pub struct ArtiHttpConnection { pub struct ArtiHttpConnection<TC: TlsConn> { /// The stream #[pin] inner: DataStream, inner: MaybeHttpsStream<TC>, } /// The actual actual stream; might be TLS, might not #[pin_project(project = MaybeHttpsStreamProj)] enum MaybeHttpsStream<TC: TlsConn> { /// http Http(Pin<Box<DataStream>>), // Tc:TlsStream is generally boxed; box this one too /// https Https(#[pin] TC::TlsStream), } impl Connection for ArtiHttpConnection { impl<TC: TlsConn> Connection for ArtiHttpConnection<TC> { fn connected(&self) -> Connected { Connected::new() } Loading @@ -121,50 +151,83 @@ impl Connection for ArtiHttpConnection { // These trait implementations just defer to the inner `DataStream`; the wrapper type is just // there to implement the `Connection` trait. impl AsyncRead for ArtiHttpConnection { impl<TC: TlsConn> AsyncRead for ArtiHttpConnection<TC> { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<Result<(), std::io::Error>> { self.project().inner.poll_read(cx, buf) match self.project().inner.project() { MaybeHttpsStreamProj::Http(ds) => ds.as_mut().poll_read(cx, buf), MaybeHttpsStreamProj::Https(t) => t.poll_read(cx, buf), } } } impl AsyncWrite for ArtiHttpConnection { impl<TC: TlsConn> AsyncWrite for ArtiHttpConnection<TC> { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll<Result<usize, Error>> { self.project().inner.poll_write(cx, buf) match self.project().inner.project() { MaybeHttpsStreamProj::Http(ds) => ds.as_mut().poll_write(cx, buf), MaybeHttpsStreamProj::Https(t) => t.poll_write(cx, buf), } } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Error>> { self.project().inner.poll_flush(cx) match self.project().inner.project() { MaybeHttpsStreamProj::Http(ds) => ds.as_mut().poll_flush(cx), MaybeHttpsStreamProj::Https(t) => t.poll_flush(cx), } } fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Error>> { self.project().inner.poll_shutdown(cx) match self.project().inner.project() { MaybeHttpsStreamProj::Http(ds) => ds.as_mut().poll_shutdown(cx), MaybeHttpsStreamProj::Https(t) => t.poll_shutdown(cx), } } } /// Convert uri to host and port fn uri_to_host_port(uri: Uri) -> Result<(String, u16), ConnectionError> { if uri.scheme() != Some(&Scheme::HTTP) { #[derive(Debug, Clone, Copy, Eq, PartialEq)] /// Are we doing TLS? enum UseTls { /// No Bare, /// Yes Tls, } /// Convert uri to http[s] host and port, and whether to do tls fn uri_to_host_port_tls(uri: Uri) -> Result<(String, u16, UseTls), ConnectionError> { let use_tls = { // Scheme doesn't derive PartialEq so can't be matched on let scheme = uri.scheme(); if scheme == Some(&Scheme::HTTP) { UseTls::Bare } else if scheme == Some(&Scheme::HTTPS) { UseTls::Tls } else { return Err(ConnectionError::UnsupportedUriScheme { uri }); } }; let host = match uri.host() { Some(h) => h, _ => return Err(ConnectionError::MissingHostname { uri }), }; let port = uri.port().map(|x| x.as_u16()).unwrap_or(80); let port = uri.port().map(|x| x.as_u16()).unwrap_or(match use_tls { UseTls::Tls => 443, UseTls::Bare => 80, }); Ok((host.to_owned(), port)) Ok((host.to_owned(), port, use_tls)) } impl<R: Runtime> Service<Uri> for ArtiHttpConnector<R> { type Response = ArtiHttpConnection; impl<R: Runtime, TC: TlsConn> Service<Uri> for ArtiHttpConnector<R, TC> { type Response = ArtiHttpConnection<TC>; type Error = ConnectionError; #[allow(clippy::type_complexity)] type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>; Loading @@ -178,15 +241,28 @@ impl<R: Runtime> Service<Uri> for ArtiHttpConnector<R> { // underlying handles required to make Tor connections internally). // We use this to avoid the returned future having to borrow `self`. let client = self.client.clone(); let tls_conn = self.tls_conn.clone(); Box::pin(async move { // Extract the host and port to connect to from the URI. let (host, port) = uri_to_host_port(req)?; let (host, port, use_tls) = uri_to_host_port_tls(req)?; // Initiate a new Tor connection, producing a `DataStream` if successful. let addr = (&host as &str, port) .into_tor_addr() .map_err(arti_client::Error::from)?; let ds = client.connect(addr).await?; Ok(ArtiHttpConnection { inner: ds }) let inner = match use_tls { UseTls::Tls => { let conn = tls_conn .connect_impl_tls_stream(&host, ds) .await .map_err(|e| ConnectionError::TLS(e.into()))?; MaybeHttpsStream::Https(conn) } UseTls::Bare => MaybeHttpsStream::Http(Box::new(ds).into()), }; Ok(ArtiHttpConnection { inner }) }) } }