Merge branch 'stuff-prefs' into 'main'

Provide TorClient::set_default_prefs and clone_with_prefs

Closes #290

See merge request tpo/core/arti!250
This commit is contained in:
Nick Mathewson 2022-01-20 14:36:41 +00:00
commit a57b4962b3
2 changed files with 64 additions and 23 deletions

View File

@ -45,7 +45,13 @@ pub struct TorClient<R: Runtime> {
/// Asynchronous runtime object.
runtime: R,
/// Default isolation token for streams through this client.
///
/// This is eventually used for `owner_token` in `tor-circmgr/src/usage.rs`, and is orthogonal
/// to the `stream_token` which comes from `connect_prefs` (or a passed-in `ConnectPrefs`).
/// (ie, both must be the same to share a circuit).
client_isolation: IsolationToken,
/// Connection preferences. Starts out as `Default`, Inherited by our clones.
connect_prefs: ConnectPrefs,
/// Circuit manager for keeping our circuits up to date and building
/// them on-demand.
circmgr: Arc<tor_circmgr::CircMgr<R>>,
@ -163,6 +169,14 @@ impl ConnectPrefs {
/// Indicate which other connections might use the same circuit
/// as this one.
///
/// By default all connections made on all clones of a `TorClient` may share connections.
/// Connections made with a particular `isolation_group` may share circuits with each other.
///
/// This connection preference is orthogonal to isolation established by
/// [`TorClient::isolated_client`]. Connections made with an `isolated_client` (and its
/// clones) will not share circuits with the original client, even if the same
/// `isolation_group` is specified via the `ConnectionPrefs` in force.
pub fn set_isolation_group(&mut self, isolation_group: IsolationToken) -> &mut Self {
self.isolation_group = Some(isolation_group);
self
@ -293,6 +307,7 @@ impl<R: Runtime> TorClient<R> {
Ok(TorClient {
runtime,
client_isolation,
connect_prefs: Default::default(),
circmgr,
dirmgr,
statemgr,
@ -380,6 +395,9 @@ impl<R: Runtime> TorClient<R> {
/// Calling this function is usually preferable to creating a
/// completely separate TorClient instance, since it can share its
/// internals with the existing `TorClient`.
///
/// (Connections made with clones of the returned `TorClient` may
/// share circuits with each other.)
#[must_use]
pub fn isolated_client(&self) -> TorClient<R> {
let mut result = self.clone();
@ -393,29 +411,28 @@ impl<R: Runtime> TorClient<R> {
/// Note that because Tor prefers to do DNS resolution on the remote
/// side of the network, this function takes its address as a string.
pub async fn connect<A: IntoTorAddr>(&self, target: A) -> Result<DataStream> {
self.connect_with_prefs(target, ConnectPrefs::default())
.await
self.connect_with_prefs(target, &self.connect_prefs).await
}
/// Launch an anonymized connection to the provided address and
/// port over the Tor network with connection preference flags.
/// port over the Tor network, with explicit connection preferences.
///
/// Note that because Tor prefers to do DNS resolution on the remote
/// side of the network, this function takes its address as a string.
pub async fn connect_with_prefs<A: IntoTorAddr>(
&self,
target: A,
flags: ConnectPrefs,
prefs: &ConnectPrefs,
) -> Result<DataStream> {
let addr = target.into_tor_addr()?;
addr.enforce_config(&self.addrcfg.get())?;
let (addr, port) = addr.into_string_and_port();
let exit_ports = [flags.wrap_target_port(port)];
let circ = self.get_or_launch_exit_circ(&exit_ports, &flags).await?;
let exit_ports = [prefs.wrap_target_port(port)];
let circ = self.get_or_launch_exit_circ(&exit_ports, prefs).await?;
info!("Got a circuit for {}:{}", addr, port);
let stream_future = circ.begin_stream(&addr, port, Some(flags.stream_parameters()));
let stream_future = circ.begin_stream(&addr, port, Some(prefs.stream_parameters()));
// This timeout is needless but harmless for optimistic streams.
let stream = self
.runtime
@ -425,22 +442,47 @@ impl<R: Runtime> TorClient<R> {
Ok(stream)
}
/// On success, return a list of IP addresses.
pub async fn resolve(&self, hostname: &str) -> Result<Vec<IpAddr>> {
self.resolve_with_prefs(hostname, ConnectPrefs::default())
.await
/// Sets the default preferences for future connections made with this client.
///
/// The preferences set with this function will be inherited by clones of this client, but
/// updates to the preferences in those clones will not propagate back to the original. I.e.,
/// the preferences are copied by `clone`.
///
/// Connection preferences always override configuration, even configuration set later
/// (eg, by a config reload).
//
// This function is private just because we're not sure we want to provide this API.
// https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/250#note_2771238
fn set_connect_prefs(&mut self, connect_prefs: ConnectPrefs) {
self.connect_prefs = connect_prefs;
}
/// On success, return a list of IP addresses, but use flags.
/// Provides a new handle on this client, but with adjusted default preferences.
///
/// Connections made with e.g. [`connect`](TorClient::connect) on the returned handle will use
/// `connect_prefs`. This is a convenience wrapper for `clone` and `set_connect_prefs`.
#[must_use]
pub fn clone_with_prefs(&self, connect_prefs: ConnectPrefs) -> Self {
let mut result = self.clone();
result.set_connect_prefs(connect_prefs);
result
}
/// On success, return a list of IP addresses.
pub async fn resolve(&self, hostname: &str) -> Result<Vec<IpAddr>> {
self.resolve_with_prefs(hostname, &self.connect_prefs).await
}
/// On success, return a list of IP addresses, but use prefs.
pub async fn resolve_with_prefs(
&self,
hostname: &str,
flags: ConnectPrefs,
prefs: &ConnectPrefs,
) -> Result<Vec<IpAddr>> {
let addr = (hostname, 0).into_tor_addr()?;
addr.enforce_config(&self.addrcfg.get())?;
let circ = self.get_or_launch_exit_circ(&[], &flags).await?;
let circ = self.get_or_launch_exit_circ(&[], prefs).await?;
let resolve_future = circ.resolve(hostname);
let addrs = self
@ -455,8 +497,7 @@ impl<R: Runtime> TorClient<R> {
///
/// On success, return a list of hostnames.
pub async fn resolve_ptr(&self, addr: IpAddr) -> Result<Vec<String>> {
self.resolve_ptr_with_prefs(addr, ConnectPrefs::default())
.await
self.resolve_ptr_with_prefs(addr, &self.connect_prefs).await
}
/// Perform a remote DNS reverse lookup with the provided IP address.
@ -465,9 +506,9 @@ impl<R: Runtime> TorClient<R> {
pub async fn resolve_ptr_with_prefs(
&self,
addr: IpAddr,
flags: ConnectPrefs,
prefs: &ConnectPrefs,
) -> Result<Vec<String>> {
let circ = self.get_or_launch_exit_circ(&[], &flags).await?;
let circ = self.get_or_launch_exit_circ(&[], prefs).await?;
let resolve_ptr_future = circ.resolve_ptr(addr);
let hostnames = self
@ -504,7 +545,7 @@ impl<R: Runtime> TorClient<R> {
async fn get_or_launch_exit_circ(
&self,
exit_ports: &[TargetPort],
flags: &ConnectPrefs,
prefs: &ConnectPrefs,
) -> Result<ClientCirc> {
let dir = self.dirmgr.netdir();
@ -513,7 +554,7 @@ impl<R: Runtime> TorClient<R> {
// Always consider our client_isolation.
b.owner_token(self.client_isolation);
// Consider stream isolation too, if it's set.
if let Some(tok) = flags.isolation_group() {
if let Some(tok) = prefs.isolation_group() {
b.stream_token(tok);
}
// Failure should be impossible with this builder.

View File

@ -188,7 +188,7 @@ where
// The SOCKS request wants us to connect to a given address.
// So, launch a connection over Tor.
let tor_stream = tor_client
.connect_with_prefs((addr.clone(), port), prefs)
.connect_with_prefs((addr.clone(), port), &prefs)
.await;
let tor_stream = match tor_stream {
Ok(s) => s,
@ -226,7 +226,7 @@ where
SocksCmd::RESOLVE => {
// We've been asked to perform a regular hostname lookup.
// (This is a tor-specific SOCKS extension.)
let addrs = tor_client.resolve_with_prefs(&addr, prefs).await?;
let addrs = tor_client.resolve_with_prefs(&addr, &prefs).await?;
if let Some(addr) = addrs.first() {
let reply = request.reply(
tor_socksproto::SocksStatus::SUCCEEDED,
@ -247,7 +247,7 @@ where
return Err(anyhow!(e));
}
};
let hosts = tor_client.resolve_ptr_with_prefs(addr, prefs).await?;
let hosts = tor_client.resolve_ptr_with_prefs(addr, &prefs).await?;
if let Some(host) = hosts.into_iter().next() {
let reply = request.reply(
tor_socksproto::SocksStatus::SUCCEEDED,