#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
use std::collections::HashSet;
use anyhow::Result;
use arti_client::isolation::IsolationHelper;
use arti_client::{IsolationToken, StreamPrefs, TorClient, TorClientConfig};
use tokio_crate as tokio;
use futures::io::{AsyncReadExt, AsyncWriteExt};
#[derive(Debug, Clone, Copy)]
enum IsolateSensitive {
Sensitive(IsolationToken),
Innocent,
}
impl IsolateSensitive {
fn new_sensitive() -> Self {
IsolateSensitive::Sensitive(IsolationToken::new())
}
}
impl IsolationHelper for IsolateSensitive {
fn compatible_same_type(&self, other: &Self) -> bool {
match (self, other) {
(IsolateSensitive::Sensitive(i), IsolateSensitive::Sensitive(j)) => {
i.compatible_same_type(j)
}
_ => true,
}
}
fn join_same_type(&self, other: &Self) -> Option<Self> {
match (self, other) {
(IsolateSensitive::Sensitive(i), IsolateSensitive::Sensitive(j)) => {
i.join_same_type(j).map(IsolateSensitive::Sensitive)
}
(res @ IsolateSensitive::Sensitive(_), _)
| (_, res @ IsolateSensitive::Sensitive(_)) => Some(*res),
_ => Some(IsolateSensitive::Innocent),
}
}
}
#[derive(Debug, Clone)]
struct IsolateOverused<T, const MAX_USAGE: usize> {
usages: HashSet<T>,
}
impl<T: Eq + std::hash::Hash, const MAX_USAGE: usize> IsolateOverused<T, MAX_USAGE> {
fn new_with_usage(usage: T) -> Self {
let mut usages = HashSet::new();
usages.insert(usage);
IsolateOverused { usages }
}
}
impl<T: Eq + std::hash::Hash + Clone, const MAX_USAGE: usize> IsolationHelper
for IsolateOverused<T, MAX_USAGE>
{
fn compatible_same_type(&self, other: &Self) -> bool {
self.usages.union(&other.usages).count() <= MAX_USAGE
}
fn join_same_type(&self, other: &Self) -> Option<Self> {
let usages: HashSet<_> = self.usages.union(&other.usages).cloned().collect();
if usages.len() <= MAX_USAGE {
Some(IsolateOverused { usages })
} else {
None
}
}
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt::init();
let config = TorClientConfig::default();
eprintln!("connecting to Tor...");
let tor_client = TorClient::create_bootstrapped(config).await?;
let innocent = {
let mut pref = StreamPrefs::new();
pref.set_isolation(IsolateSensitive::Innocent);
pref
};
let sensitive_1 = {
let mut pref = StreamPrefs::new();
pref.set_isolation(IsolateSensitive::new_sensitive());
pref
};
let sensitive_2 = {
let mut pref = StreamPrefs::new();
pref.set_isolation(IsolateSensitive::new_sensitive());
pref
};
eprintln!("sending a bunch of sensitive and innocent requests");
let mut stream = tor_client
.connect_with_prefs(("example.net", 80), &innocent)
.await?;
send_request(&mut stream, "example.net").await?;
let mut stream = tor_client
.connect_with_prefs(("example.com", 80), &sensitive_1)
.await?;
send_request(&mut stream, "example.com").await?;
let mut stream = tor_client
.connect_with_prefs(("example.com", 80), &sensitive_2)
.await?;
send_request(&mut stream, "example.com").await?;
let mut stream = tor_client
.connect_with_prefs(("example.com", 80), &sensitive_1)
.await?;
send_request(&mut stream, "example.com").await?;
let first_usage = {
let mut pref = StreamPrefs::new();
pref.set_isolation(IsolateOverused::<_, 3>::new_with_usage("first usage"));
pref
};
let second_usage = {
let mut pref = StreamPrefs::new();
pref.set_isolation(IsolateOverused::<_, 3>::new_with_usage("second usage"));
pref
};
let third_usage = {
let mut pref = StreamPrefs::new();
pref.set_isolation(IsolateOverused::<_, 3>::new_with_usage("third usage"));
pref
};
let fourth_usage = {
let mut pref = StreamPrefs::new();
pref.set_isolation(IsolateOverused::<_, 3>::new_with_usage("fourth usage"));
pref
};
let mut stream = tor_client
.connect_with_prefs(("example.net", 80), &first_usage)
.await?;
send_request(&mut stream, "example.net").await?;
let mut stream = tor_client
.connect_with_prefs(("example.org", 80), &second_usage)
.await?;
send_request(&mut stream, "example.org").await?;
let mut stream = tor_client
.connect_with_prefs(("example.com", 80), &third_usage)
.await?;
send_request(&mut stream, "example.com").await?;
let mut stream = tor_client
.connect_with_prefs(("example.net", 80), &fourth_usage)
.await?;
send_request(&mut stream, "example.net").await?;
Ok(())
}
async fn send_request(stream: &mut arti_client::DataStream, host: &str) -> Result<()> {
stream
.write_all(
format!(
"GET / HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n",
host
)
.as_bytes(),
)
.await?;
stream.flush().await?;
let mut buf = Vec::new();
stream.read_to_end(&mut buf).await?;
println!("{}", String::from_utf8_lossy(&buf));
Ok(())
}