Merge branch 'onion-api-highlevel' into 'main'
Onion-service APIs: circmgr, hsclient, hsservice See merge request tpo/core/arti!972
This commit is contained in:
commit
3b2848f904
|
@ -3673,6 +3673,7 @@ dependencies = [
|
|||
"tor-config",
|
||||
"tor-error",
|
||||
"tor-guardmgr",
|
||||
"tor-hscrypto",
|
||||
"tor-linkspec",
|
||||
"tor-llcrypto",
|
||||
"tor-netdir",
|
||||
|
@ -3883,6 +3884,21 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tor-hsclient"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"rand_core 0.6.4",
|
||||
"thiserror",
|
||||
"tor-circmgr",
|
||||
"tor-hscrypto",
|
||||
"tor-llcrypto",
|
||||
"tor-netdir",
|
||||
"tor-proto",
|
||||
"tor-rtcompat",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tor-hscrypto"
|
||||
version = "0.1.0"
|
||||
|
@ -3892,6 +3908,21 @@ dependencies = [
|
|||
"tor-llcrypto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tor-hsservice"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"rand_core 0.6.4",
|
||||
"thiserror",
|
||||
"tor-circmgr",
|
||||
"tor-hscrypto",
|
||||
"tor-llcrypto",
|
||||
"tor-netdir",
|
||||
"tor-proto",
|
||||
"tor-rtcompat",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tor-linkspec"
|
||||
version = "0.6.0"
|
||||
|
|
|
@ -38,6 +38,8 @@ members = [
|
|||
"crates/tor-circmgr",
|
||||
"crates/tor-dirclient",
|
||||
"crates/tor-dirmgr",
|
||||
"crates/tor-hsclient",
|
||||
"crates/tor-hsservice",
|
||||
"crates/arti-client",
|
||||
"crates/arti-config",
|
||||
"crates/arti-hyper",
|
||||
|
|
|
@ -21,8 +21,11 @@ specific-relay = []
|
|||
#
|
||||
# These APIs are not covered by semantic versioning. Using this
|
||||
# feature voids your "semver warrantee".
|
||||
experimental = ["experimental-api"]
|
||||
experimental = ["experimental-api", "onion-client", "onion-service"]
|
||||
experimental-api = []
|
||||
onion-client = ["onion-common"]
|
||||
onion-service = ["onion-common"]
|
||||
onion-common = ["tor-hscrypto"]
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.2"
|
||||
|
@ -47,6 +50,7 @@ tor-chanmgr = { path = "../tor-chanmgr", version = "0.8.0" }
|
|||
tor-config = { path = "../tor-config", version = "0.7.0" }
|
||||
tor-error = { path = "../tor-error", version = "0.4.0" }
|
||||
tor-guardmgr = { path = "../tor-guardmgr", version = "0.8.0" }
|
||||
tor-hscrypto = { path = "../tor-hscrypto", version = "0.1.0", optional = true }
|
||||
tor-linkspec = { path = "../tor-linkspec", version = "0.6.0" }
|
||||
tor-netdir = { path = "../tor-netdir", version = "0.7.0" }
|
||||
tor-netdoc = { path = "../tor-netdoc", version = "0.6.0" }
|
||||
|
|
|
@ -60,6 +60,8 @@ mod err;
|
|||
mod impls;
|
||||
pub mod isolation;
|
||||
mod mgr;
|
||||
#[cfg(feature = "onion-client")]
|
||||
mod onion_connector;
|
||||
pub mod path;
|
||||
mod preemptive;
|
||||
mod timeouts;
|
||||
|
@ -67,6 +69,9 @@ mod usage;
|
|||
|
||||
pub use err::Error;
|
||||
pub use isolation::IsolationToken;
|
||||
#[cfg(feature = "onion-client")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "onion-client")))]
|
||||
pub use onion_connector::{OnionConnectError, OnionServiceConnector};
|
||||
use tor_guardmgr::fallback::FallbackList;
|
||||
pub use tor_guardmgr::{ClockSkewEvents, GuardMgrConfig, SkewEstimate};
|
||||
pub use usage::{TargetPort, TargetPorts};
|
||||
|
@ -206,6 +211,20 @@ impl<R: Runtime> CircMgr<R> {
|
|||
Ok(circmgr)
|
||||
}
|
||||
|
||||
/// Install a given [`OnionServiceConnector`] object to be used when making
|
||||
/// connections to an onion service.
|
||||
///
|
||||
/// (This cannot be done at construction time, since the
|
||||
/// OnionServiceConnector will have to keep a reference to this `CircMgr`.)
|
||||
#[cfg(feature = "onion-client")]
|
||||
#[allow(unused_variables, clippy::missing_panics_doc)]
|
||||
pub fn install_onion_service_connector(
|
||||
&self,
|
||||
connector: &Arc<dyn OnionServiceConnector>,
|
||||
) -> Result<()> {
|
||||
todo!() // TODO hs
|
||||
}
|
||||
|
||||
/// Launch the periodic daemon tasks required by the manager to function properly.
|
||||
///
|
||||
/// Returns a set of [`TaskHandle`]s that can be used to manage the daemon tasks.
|
||||
|
@ -410,7 +429,35 @@ impl<R: Runtime> CircMgr<R> {
|
|||
self.mgr.get_or_launch(&usage, netdir).await.map(|(c, _)| c)
|
||||
}
|
||||
|
||||
/// Return a circuit to a specific relay, suitable for using for directory downloads.
|
||||
/// Try to connect to an onion service via this circuit manager.
|
||||
///
|
||||
/// If `using_keys` is provided, then we will use those keys, in addition to
|
||||
/// any configured in our `OnionServiceConnector`, to connect to the
|
||||
/// service.
|
||||
///
|
||||
/// If we already have an existing circuit with the appropriate isolation,
|
||||
/// we will return that circuit regardless of the content of `using_keys`.
|
||||
///
|
||||
/// Requires that an `OnionServiceConnector` has been installed. If it
|
||||
/// hasn't, then we return an error.
|
||||
#[cfg(feature = "onion-client")]
|
||||
#[allow(clippy::missing_panics_doc, unused_variables)]
|
||||
pub async fn get_or_launch_onion_client(
|
||||
&self,
|
||||
service_id: tor_hscrypto::pk::OnionId,
|
||||
using_keys: Option<tor_hscrypto::pk::ClientSecretKeys>,
|
||||
isolation: StreamIsolation,
|
||||
) -> Result<ClientCirc> {
|
||||
todo!() // TODO hs
|
||||
|
||||
// The implementation should look up whether we have an appropriate
|
||||
// connected rendezvous circuit built or in progress in our CircMgr. If
|
||||
// we do, we should return it or wait for it. Otherwise we should
|
||||
// delegate to our OnionServiceConnector to build it.
|
||||
}
|
||||
|
||||
/// Return a circuit to a specific relay, suitable for using for direct
|
||||
/// (one-hop) directory downloads.
|
||||
///
|
||||
/// This could be used, for example, to download a descriptor for a bridge.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "specific-relay")))]
|
||||
|
@ -427,6 +474,28 @@ impl<R: Runtime> CircMgr<R> {
|
|||
.map(|(c, _)| c)
|
||||
}
|
||||
|
||||
/// Create and return a new (typically anonymous) circuit whose last hop is
|
||||
/// `target`.
|
||||
///
|
||||
/// This circuit is guaranteed not to have been used for any traffic
|
||||
/// previously, and it will not be given out for any other requests in the
|
||||
/// future unless explicitly re-registered with a circuit manager.
|
||||
///
|
||||
/// Used to implement onion service clients and services.
|
||||
#[cfg(feature = "onion-common")]
|
||||
#[allow(unused_variables, clippy::missing_panics_doc)]
|
||||
pub async fn launch_specific_isolated(
|
||||
&self,
|
||||
target: tor_linkspec::OwnedCircTarget,
|
||||
// TODO hs: this should at least be an enum to define what kind of
|
||||
// circuit we want, in case we have different rules for different types.
|
||||
// It might also need to include a "anonymous?" flag for supporting
|
||||
// single onion services.
|
||||
preferences: (),
|
||||
) -> Result<ClientCirc> {
|
||||
todo!() // TODO hs implement.
|
||||
}
|
||||
|
||||
/// Launch circuits preemptively, using the preemptive circuit predictor's
|
||||
/// predictions.
|
||||
///
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
//! Declare the `OnionServiceConnector` trait.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use thiserror::Error;
|
||||
use tor_proto::circuit::ClientCirc;
|
||||
|
||||
/// A trait representing the ability to make a connection to an onion service.
|
||||
///
|
||||
/// This is defined in `tor-circmgr`, since `tor-circmgr` uses an instance of
|
||||
/// this object to connect to onion services.
|
||||
#[async_trait]
|
||||
pub trait OnionServiceConnector {
|
||||
/// Try to launch a connection to a given onion service.
|
||||
async fn create_connection(
|
||||
&self,
|
||||
service_id: tor_hscrypto::pk::OnionId,
|
||||
using_keys: Option<tor_hscrypto::pk::ClientSecretKeys>,
|
||||
// TODO hs: If we want to support cache isolation, we may need to pass
|
||||
// an additional argument here.
|
||||
) -> Result<ClientCirc, OnionConnectError>;
|
||||
}
|
||||
|
||||
/// An error returned when constructing an onion service.
|
||||
#[derive(Debug, Clone, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum OnionConnectError {
|
||||
// TODO hs add variants.
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
[package]
|
||||
name = "tor-hsclient"
|
||||
version = "0.1.0"
|
||||
authors = ["The Tor Project, Inc.", "Nick Mathewson <nickm@torproject.org>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.60"
|
||||
license = "MIT OR Apache-2.0"
|
||||
homepage = "https://gitlab.torproject.org/tpo/core/arti/-/wikis/home"
|
||||
description = "Arti's implementation of an onion service client"
|
||||
keywords = ["tor", "arti", "cryptography"]
|
||||
categories = ["cryptography"]
|
||||
repository = "https://gitlab.torproject.org/tpo/core/arti.git/"
|
||||
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.2"
|
||||
rand_core = "0.6.2"
|
||||
thiserror = "1"
|
||||
tor-circmgr = { version = "0.7.0", path = "../tor-circmgr", features = ["onion-client"] }
|
||||
tor-hscrypto = { version = "0.1.0", path = "../tor-hscrypto" }
|
||||
tor-llcrypto = { version = "0.4.0", path = "../tor-llcrypto" }
|
||||
tor-netdir = { version = "0.7.0", path = "../tor-netdir" }
|
||||
tor-proto = { version = "0.8.0", path = "../tor-proto" }
|
||||
tor-rtcompat = { version = "0.8.0", path = "../tor-rtcompat" }
|
||||
|
||||
[dev-dependencies]
|
|
@ -0,0 +1,17 @@
|
|||
# tor-hsclient
|
||||
|
||||
Core implementation for onion services client.
|
||||
|
||||
## EXPERIMENTAL DRAFT
|
||||
|
||||
This crate is a work in progress; it is not the least bit complete.
|
||||
|
||||
Right now, it does not even work: it's only here so that we can prototype
|
||||
our APIs.
|
||||
|
||||
## ARCHITECTURAL NOTE
|
||||
|
||||
This crate creates circuits to onion circuits, but does not remember them: it is
|
||||
the circmgr's job to remember circuits. The tor-circmgr crate uses this module
|
||||
indirectly, via a trait that it defines.
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
//! Manage a set of private keys for a client to authenticate to one or more
|
||||
//! onion services.
|
||||
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
|
||||
use tor_hscrypto::pk::{ClientSecretKeys, OnionId};
|
||||
|
||||
pub(crate) struct Keys {
|
||||
/// The
|
||||
keys: Mutex<HashMap<OnionId, ClientSecretKeys>>,
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
// TODO hs: Add complete suite of warnings here.
|
||||
#![allow(dead_code, unused_variables)] // TODO hs remove.
|
||||
|
||||
mod keys;
|
||||
mod state;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use std::sync::Arc;
|
||||
use tor_hscrypto::pk::{ClientSecretKeys, OnionId};
|
||||
use tor_proto::circuit::ClientCirc;
|
||||
|
||||
use tor_circmgr::{CircMgr, OnionConnectError, OnionServiceConnector};
|
||||
use tor_netdir::NetDirProvider;
|
||||
use tor_rtcompat::Runtime;
|
||||
|
||||
/// An object that negotiates connections with onion services
|
||||
pub struct HsClientConnector<R: Runtime> {
|
||||
/// A [`CircMgr`] that we use to build circuits to HsDirs, introduction
|
||||
/// points, and rendezvous points.
|
||||
//
|
||||
// TODO hs: currently this is a circular set of Arc, since the CircMgr will
|
||||
// have to hold an Arc<OnionServiceConnector>. We should make one Weak.
|
||||
//
|
||||
// TODO hs: Maybe we can make a trait that only gives a minimal "build a
|
||||
// circuit" API from CircMgr, so that we can have this be a dyn reference
|
||||
// too?
|
||||
circmgr: Arc<CircMgr<R>>,
|
||||
/// A [`NetDirProvider`] that we use to pick rendezvous points.
|
||||
//
|
||||
// TODO hs: Should this be weak too?
|
||||
netdir_provider: Arc<dyn NetDirProvider>,
|
||||
/// Information we are remembering about different onion services.
|
||||
//
|
||||
// TODO hs: if we implement cache isolation or state isolation, we might
|
||||
// need multiple instances of this.
|
||||
state: state::StateMap,
|
||||
/// A collection of private keys to be used with various onion services.
|
||||
//
|
||||
// TODO hs: we might even want multiple instances of this, depending on how
|
||||
// we decide to do isolation.
|
||||
keys: keys::Keys,
|
||||
}
|
||||
|
||||
impl<R: Runtime> HsClientConnector<R> {
|
||||
// TODO hs: Need a way to manage the set of keys.
|
||||
|
||||
// TODO hs: need a constructor here.
|
||||
|
||||
// TODO hs: need a function to clear our StateMap, or to create a new
|
||||
// isolated StateMap.
|
||||
//
|
||||
// TODO hs: Also, we need to expose that function from `TorClient`, possibly
|
||||
// in the existing isolation API, possibly in something new.
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<R: Runtime> OnionServiceConnector for HsClientConnector<R> {
|
||||
async fn create_connection(
|
||||
&self,
|
||||
service_id: OnionId,
|
||||
using_keys: Option<ClientSecretKeys>,
|
||||
) -> Result<ClientCirc, OnionConnectError> {
|
||||
todo!() // TODO hs
|
||||
|
||||
// This function must do the following, retrying as appropriate.
|
||||
// - Look up the onion descriptor in the state.
|
||||
// - Download the onion descriptor if one isn't there.
|
||||
// - In parallel:
|
||||
// - Pick a rendezvous point from the netdirprovider and launch a
|
||||
// rendezvous circuit to it. Then send ESTABLISH_INTRO.
|
||||
// - Pick a number of introduction points (1 or more) and try to
|
||||
// launch circuits to them.
|
||||
// - On a circuit to an introduction point, send an INTRODUCE1 cell.
|
||||
// - Wait for a RENDEZVOUS2 cell on the rendezvous circuit
|
||||
// - Add a virtual hop to the rendezvous circuit.
|
||||
// - Return the rendezvous circuit.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
//! Implement a cache for onion descriptors and the facility to remember a bit
|
||||
//! about onion service history.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use tor_hscrypto::pk::BlindedOnionId;
|
||||
|
||||
/// Information about onion services and our history of connecting to them.
|
||||
pub(crate) struct StateMap {
|
||||
/// A map from blinded onion identity to information about an onion service.
|
||||
///
|
||||
/// If the map is to `None`, then a download is in progress for that state's
|
||||
/// descriptor.
|
||||
members: Mutex<HashMap<BlindedOnionId, Option<State>>>,
|
||||
}
|
||||
|
||||
/// Information about our history of connecting to an onion service.
|
||||
//
|
||||
// TODO hs: We might need this to be an enum, if we want to represent "fetch
|
||||
// pending" as something with a RetryDelay. We might even want a RetryDelay
|
||||
// associated with each HsDir for the service as well!
|
||||
pub(crate) struct State {
|
||||
/// A time when we should check whether this descriptor is still the latest.
|
||||
desc_fresh_until: SystemTime,
|
||||
/// A time when we should expire this entry completely.
|
||||
expires: SystemTime,
|
||||
/// The latest known onion service descriptor for this service.
|
||||
desc: (), // TODO hs: use actual onion service descriptor type.
|
||||
/// Information about the latest status of trying to connect to this service
|
||||
/// through each of its introduction points.
|
||||
///
|
||||
ipts: (), // TODO hs: make this type real, use `RetryDelay`, etc.
|
||||
}
|
||||
|
||||
impl StateMap {
|
||||
// TODO hs: we need a way to make the entries here expire over time.
|
||||
}
|
|
@ -127,7 +127,16 @@ pub struct ClientDescAuthKey(curve25519::PublicKey);
|
|||
// The names should be something like these:
|
||||
pub struct OnionIdSecretKey(ed25519::SecretKey);
|
||||
pub struct ClientDescAuthSecretKey(curve25519::StaticSecret);
|
||||
pub struct ClientIntroAuthSecretKey(ed25519::SecretKey);
|
||||
// ... and so on.
|
||||
//
|
||||
// NOTE: We'll have to use ExpandedSecretKey as the secret key
|
||||
// for BlindedOnionIdSecretKey.
|
||||
|
||||
/// A set of keys to tell the client to use when connecting to an onion service.
|
||||
//
|
||||
// TODO hs
|
||||
pub struct ClientSecretKeys {
|
||||
desc_auth: Option<ClientDescAuthSecretKey>,
|
||||
intro_auth: Option<ClientIntroAuthSecretKey>,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
[package]
|
||||
name = "tor-hsservice"
|
||||
version = "0.1.0"
|
||||
authors = ["The Tor Project, Inc.", "Nick Mathewson <nickm@torproject.org>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.60"
|
||||
license = "MIT OR Apache-2.0"
|
||||
homepage = "https://gitlab.torproject.org/tpo/core/arti/-/wikis/home"
|
||||
description = "Arti's implementation of an onion service provider"
|
||||
keywords = ["tor", "arti", "cryptography"]
|
||||
categories = ["cryptography"]
|
||||
repository = "https://gitlab.torproject.org/tpo/core/arti.git/"
|
||||
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.2"
|
||||
rand_core = "0.6.2"
|
||||
thiserror = "1"
|
||||
tor-circmgr = { version = "0.7.0", path = "../tor-circmgr", features = ["onion-client"] }
|
||||
tor-hscrypto = { version = "0.1.0", path = "../tor-hscrypto" }
|
||||
tor-llcrypto = { version = "0.4.0", path = "../tor-llcrypto" }
|
||||
tor-netdir = { version = "0.7.0", path = "../tor-netdir" }
|
||||
tor-proto = { version = "0.8.0", path = "../tor-proto" }
|
||||
tor-rtcompat = { version = "0.8.0", path = "../tor-rtcompat" }
|
||||
|
||||
[dev-dependencies]
|
|
@ -0,0 +1,11 @@
|
|||
# tor-hsclient
|
||||
|
||||
Core implementation for onion services in arti.
|
||||
|
||||
## EXPERIMENTAL DRAFT
|
||||
|
||||
This crate is a work in progress; it is not the least bit complete.
|
||||
|
||||
Right now, it does not even work: it's only here so that we can prototype
|
||||
our APIs.
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
//! Declare an error type for the `tor-hsservice` crate.
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// An error affecting the operation of an onion service.
|
||||
#[derive(Clone, Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {}
|
|
@ -0,0 +1,45 @@
|
|||
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
// TODO hs: Add complete suite of warnings here.
|
||||
#![allow(dead_code, unused_variables)] // TODO hs remove.
|
||||
|
||||
mod err;
|
||||
mod status;
|
||||
mod streamproxy;
|
||||
mod svc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
pub use err::Error;
|
||||
pub use status::OnionServiceStatus;
|
||||
pub use svc::OnionService;
|
||||
|
||||
/// A Result type describing an onion service operation.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// An object that knows how to handle stream requests.
|
||||
#[async_trait]
|
||||
pub trait StreamHandler {
|
||||
/// Handle an incoming stream request on a given onion service.
|
||||
//
|
||||
// TODO hs: the `circ_info` argument should have data about the circuit on
|
||||
// which the request arrived. If the client authenticated, it might tell us
|
||||
// who they are. Or it might have information about how many requests
|
||||
// (and/or failed requests) we've gotten on the circuit.
|
||||
//
|
||||
// TODO hs: The `circ_info` argument should at a minimum include the
|
||||
// circuit; ideally in a form that we can get a weak reference to it, and
|
||||
// use it in the key of a `PtrWeakKeyHashMap`. (Or we could stick the info
|
||||
// in the circuit itself somehow, and access it as a Box<dyn Any>, but
|
||||
// that's a bit sketchy type-wise.)
|
||||
//
|
||||
// TODO hs: the `stream` argument should be an IncomingStream from
|
||||
// tor-proto, but that branch is not yet merged as of this writing.
|
||||
async fn handle_request(&self, circ_info: &(), stream: ());
|
||||
}
|
||||
|
||||
mod mgr {
|
||||
// TODO hs: Do we want to have the notion of a collection of onion services,
|
||||
// running in tandem? Or is that a higher-level crate, possibly a part of
|
||||
// TorClient?
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
pub struct OnionServiceStatus {
|
||||
// TODO hs Should say how many intro points are active, how many descriptors
|
||||
// are updated, whether we're "healthy", etc.
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
//! Implement a StreamHandler that proxies connections to ports, typically on
|
||||
//! localhost.
|
||||
|
||||
use std::{collections::HashMap, net::SocketAddr};
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::StreamHandler;
|
||||
|
||||
pub(crate) struct StreamProxy {
|
||||
/// Map from virtual port on the onion service to an address we connect to
|
||||
/// in order to implement that port.
|
||||
ports: HashMap<u16, SocketAddr>,
|
||||
}
|
||||
|
||||
impl StreamProxy {
|
||||
// TODO hs need a new() function. It should reject non-localhost addresses
|
||||
// by default, and have a way to override. (Alternatively, that should be
|
||||
// done in the configuration code?)
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl StreamHandler for StreamProxy {
|
||||
async fn handle_request(&self, circinfo: &(), stream: ()) {
|
||||
todo!() // TODO hs: implement
|
||||
|
||||
// - Look up the port for the incoming stream request.
|
||||
// - If no port is found, reject the request, and possibly increment a
|
||||
// counter in circinfo.
|
||||
// - Otherwise, open a TCP connection to the target address.
|
||||
// - On success, accept the stream, and launch tasks to relay traffic
|
||||
// from the stream to the TCP connection.
|
||||
// - On failure, reject the stream with an error.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use tor_circmgr::CircMgr;
|
||||
use tor_netdir::NetDirProvider;
|
||||
use tor_rtcompat::Runtime;
|
||||
|
||||
use crate::{OnionServiceStatus, Result};
|
||||
|
||||
/// A handle to an instance of an onion service.
|
||||
//
|
||||
// TODO hs: We might want to wrap this in an Arc<Mutex<>>, and have an inner
|
||||
// structure that contains these elements. Or we might want to refactor this in
|
||||
// some other way.
|
||||
pub struct OnionService<R: Runtime> {
|
||||
/// Needs some kind of configuration about: what is our identity (if we know
|
||||
/// it), is this anonymous, do we store persistent info and if so where and
|
||||
/// how, etc.
|
||||
///
|
||||
/// Authorized client public keys might be here, or they might be in a
|
||||
/// separate structure.
|
||||
config: (),
|
||||
/// A netdir provider to use in finding our directories and choosing our
|
||||
/// introduction points.
|
||||
netdir_provider: Arc<dyn NetDirProvider>,
|
||||
/// A circuit manager to use in making circuits to our introduction points,
|
||||
/// HsDirs, and rendezvous points.
|
||||
// TODO hs: Maybe we can make a trait that only gives a minimal "build a
|
||||
// circuit" API from CircMgr, so that we can have this be a dyn reference
|
||||
// too?
|
||||
circmgr: Arc<CircMgr<R>>,
|
||||
/// Private keys in actual use for this onion service.
|
||||
///
|
||||
/// TODO hs: This will need heavy refactoring.
|
||||
///
|
||||
/// TODO hs: There's a separate blinded ID, certificate, and signing key
|
||||
/// for each active time period.
|
||||
keys: (),
|
||||
/// Status for each active introduction point for this onion service.
|
||||
intro_points: Vec<()>,
|
||||
/// Status for our onion service descriptor
|
||||
descriptor_status: (),
|
||||
|
||||
/// Object that handles incoming streams from the client.
|
||||
stream_handler: Arc<dyn crate::StreamHandler>,
|
||||
}
|
||||
|
||||
impl<R: Runtime> OnionService<R> {
|
||||
/// Create (but do not launch) a new onion service.
|
||||
pub fn new(config: (), netdir_provider: (), circmgr: ()) -> Self {
|
||||
todo!(); // TODO hs
|
||||
}
|
||||
|
||||
/// Change the configuration of this onion service.
|
||||
///
|
||||
/// (Not everything can be changed here. At the very least we'll need to say
|
||||
/// that the identity of a service is fixed. We might want to make the
|
||||
/// storage backing this, and the anonymity status, unchangeable.)
|
||||
pub fn reconfigure(&self, new_config: ()) -> Result<()> {
|
||||
todo!() // TODO hs
|
||||
}
|
||||
|
||||
/// Tell this onion service about some new short-term keys it can use.
|
||||
pub fn add_keys(&self, keys: ()) -> Result<()> {
|
||||
todo!() // TODO hs
|
||||
}
|
||||
|
||||
/// Return the current status of this onion service.
|
||||
pub fn status(&self) -> OnionServiceStatus {
|
||||
todo!() // TODO hs
|
||||
}
|
||||
// TODO hs let's also have a function that gives you a stream of Status
|
||||
// changes? Or use a publish-based watcher?
|
||||
|
||||
/// Tell this onion service to begin running.
|
||||
pub fn launch(&self) -> Result<()> {
|
||||
todo!() // TODO hs
|
||||
|
||||
// This needs to launch at least the following tasks:
|
||||
//
|
||||
// - If we decide to use separate disk-based key provisioning, a task to
|
||||
// monitor our keys directory.
|
||||
// - If we own our identity key, a task to generate per-period sub-keys as
|
||||
// needed.
|
||||
// - A task to make sure that we have enough introduction point circuits
|
||||
// at all times, and launch new ones as needed.
|
||||
// - A task to see whether we have an up-to-date descriptor uploaded for
|
||||
// each supported time period to every HsDir listed for us in the
|
||||
// current directory, and if not, regenerate and upload our descriptor
|
||||
// as needed.
|
||||
// - A task to receive introduction requests from our introduction
|
||||
// points, decide whether to answer them, and if so launch a new
|
||||
// rendezvous task to:
|
||||
// - finish the cryptographic handshake
|
||||
// - build a circuit to the rendezvous point
|
||||
// - Send the RENDEZVOUS1 reply
|
||||
// - Add a virtual hop to the rendezvous circuit
|
||||
// - Launch a new task to handle BEGIN requests on the rendezvous
|
||||
// circuit, using our StreamHandler.
|
||||
}
|
||||
|
||||
/// Tell this onion service to stop running.
|
||||
///
|
||||
/// It can be restarted with launch().
|
||||
///
|
||||
/// You can also shut down an onion service completely by dropping the last
|
||||
/// Clone of it.
|
||||
pub fn stop(&self) -> Result<()> {
|
||||
todo!() // TODO hs
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue