Add an isolate_client() function to create an isolated TorClient.

When two TorClients are isolated, their streams shouldn't share
circuits, even though they share internal circuit and guard state.
This commit is contained in:
Nick Mathewson 2021-10-25 12:02:44 -04:00
parent 84f81d14eb
commit 16f6ee4b54
1 changed files with 34 additions and 15 deletions

View File

@ -36,6 +36,8 @@ use tracing::{debug, error, info, warn};
pub struct TorClient<R: Runtime> {
/// Asynchronous runtime object.
runtime: R,
/// Default isolation token for streams through this client.
client_isolation: IsolationToken,
/// Circuit manager for keeping our circuits up to date and building
/// them on-demand.
circmgr: Arc<tor_circmgr::CircMgr<R>>,
@ -46,12 +48,12 @@ pub struct TorClient<R: Runtime> {
}
/// Preferences for how to route a stream over the Tor network.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Default)]
pub struct ConnectPrefs {
/// What kind of IPv6/IPv4 we'd prefer, and how strongly.
ip_ver_pref: IpVersionPreference,
/// Id of the isolation group the connection should be part of
isolation_group: IsolationToken,
isolation_group: Option<IsolationToken>,
}
impl ConnectPrefs {
@ -114,28 +116,19 @@ impl ConnectPrefs {
/// Indicate which other connections might use the same circuit
/// as this one.
pub fn set_isolation_group(&mut self, isolation_group: IsolationToken) -> &mut Self {
self.isolation_group = isolation_group;
self.isolation_group = Some(isolation_group);
self
}
/// Return a u64 to describe which connections might use
/// Return a token to describe which connections might use
/// the same circuit as this one.
fn isolation_group(&self) -> IsolationToken {
fn isolation_group(&self) -> Option<IsolationToken> {
self.isolation_group
}
// TODO: Add some way to be IPFlexible, and require exit to support both.
}
impl Default for ConnectPrefs {
fn default() -> Self {
ConnectPrefs {
ip_ver_pref: Default::default(),
isolation_group: IsolationToken::no_isolation(),
}
}
}
impl<R: Runtime> TorClient<R> {
/// Bootstrap a network connection configured by `dir_cfg` and `circ_cfg`.
///
@ -190,14 +183,35 @@ impl<R: Runtime> TorClient<R> {
Arc::downgrade(&dirmgr),
))?;
let client_isolation = IsolationToken::new();
Ok(TorClient {
runtime,
client_isolation,
circmgr,
dirmgr,
addrcfg: addr_cfg,
})
}
/// Return a new isolated `TorClient` instance.
///
/// The two `TorClient`s will share some internal state, but their
/// streams will haver share circuits with one another.
///
/// Use this function when you want separate parts of your program to
/// each have a TorClient handle, but where you don't want their
/// activities to be linkable to one another over the Tor network.
///
/// Calling this function is usually preferable to creating a
/// completely separate TorClient instance, since it can share its
/// internals with the existing `TorClient`.
pub fn isolated_client(&self) -> TorClient<R> {
let mut result = self.clone();
result.client_isolation = IsolationToken::new();
result
}
/// Launch an anonymized connection to the provided address and
/// port over the Tor network.
///
@ -303,9 +317,14 @@ impl<R: Runtime> TorClient<R> {
flags: &ConnectPrefs,
) -> Result<Arc<ClientCirc>> {
let dir = self.dirmgr.netdir();
// XXXX: this isn't what we really want. We'd like to have _both_
// of these tokens considered.
let isolation = flags.isolation_group().unwrap_or(self.client_isolation);
let circ = self
.circmgr
.get_or_launch_exit(dir.as_ref().into(), exit_ports, flags.isolation_group())
.get_or_launch_exit(dir.as_ref().into(), exit_ports, isolation)
.await
.map_err(|_| Error::Internal("Unable to launch circuit"))?;
drop(dir); // This decreases the refcount on the netdir.