//! Principal types for onion services. mod netdir; use std::sync::{Arc, Mutex}; use futures::channel::mpsc; use futures::channel::oneshot; use futures::Stream; use tor_circmgr::hspool::HsCircPool; use tor_config::ReconfigureError; use tor_error::Bug; use tor_keymgr::KeyMgr; use tor_llcrypto::pk::curve25519; use tor_netdir::NetDirProvider; use tor_rtcompat::Runtime; use crate::ipt_mgr::IptManager; use crate::ipt_set::IptsManagerView; use crate::svc::publish::Publisher; use crate::OnionServiceConfig; use crate::OnionServiceStatus; use crate::RendRequest; use crate::StartupError; pub(crate) mod ipt_establish; pub(crate) mod publish; pub(crate) mod rend_handshake; /// Convenience alias for link specifiers of an intro point pub(crate) type LinkSpecs = Vec; /// Convenient type alias for an ntor public key // TODO HSS maybe this should be `tor_proto::crypto::handshake::ntor::NtorPublicKey`? type NtorPublicKey = curve25519::PublicKey; /// A handle to an instance of an onion service. // // TODO HSS: Write more. // // (APIs should return Arc) pub struct OnionService { /// The mutable implementation details of this onion service. inner: Mutex, } /// Implementation details for an onion service. struct SvcInner { /// Configuration information about this service. config_tx: postage::watch::Sender>, /// A keymgr used to look up our keys and store new medium-term keys. // // TODO HSS: Do we actually need this in this structure? keymgr: Arc, /// A oneshot that will be dropped when this object is dropped. shutdown_tx: oneshot::Sender, /// Handles that we'll take ownership of when launching the service. /// /// (TODO HSS: Having to consume this may indicate a design problem.) unlaunched: Option<( mpsc::Receiver, Box, )>, } /// Objects and handles needed to launch an onion service. struct ForLaunch { /// An unlaunched handle for the HsDesc publisher. /// /// This publisher is responsible for determining when we need to upload a /// new set of HsDescs, building them, and publishing them at the correct /// HsDirs. publisher: Publisher, /// Our handler for the introduction point manager. /// /// This manager is responsible for selecting introduction points, /// maintaining our connections to them, and telling the publisher which ones /// are publicly available. ipt_mgr: IptManager>, /// A handle used by the ipt manager to send Ipts to the publisher. /// /// ipt_mgr_view: IptsManagerView, } /// Private trait used to type-erase `ForLaunch`, so that we don't need to /// parameterize OnionService on ``. trait Launchable: Send + Sync { /// Launch fn launch(self: Box) -> Result<(), StartupError>; } impl Launchable for ForLaunch { fn launch(self: Box) -> Result<(), StartupError> { self.ipt_mgr.launch_background_tasks(self.ipt_mgr_view)?; self.publisher.launch()?; Ok(()) } } impl OnionService { /// Create (but do not launch) a new onion service. pub fn new( runtime: R, config: OnionServiceConfig, netdir_provider: Arc, circ_pool: Arc>, keymgr: Arc, statemgr: S, ) -> Result, StartupError> where R: Runtime, S: tor_persist::StateMgr + Send + Sync + 'static, { let nickname = config.name.clone(); // TODO HSS: Maybe, adjust tor_persist::fs to handle subdirectories, and // use onion/{nickname}? let storage_key = format!("onion_svc_{nickname}"); // TODO HSS-IPT-PERSIST: Use this handle, and use a real struct type instead. let storage_handle: Arc> = statemgr.create_handle(storage_key); let (rend_req_tx, rend_req_rx) = mpsc::channel(32); let (shutdown_tx, shutdown_rx) = oneshot::channel(); let (config_tx, config_rx) = postage::watch::channel_with(Arc::new(config)); // TODO HSS: How do I give ipt_mgr_view to ipt_mgr? Does IptManager even take // one of these? let (ipt_mgr_view, publisher_view) = crate::ipt_set::ipts_channel(None); let ipt_mgr = IptManager::new( runtime.clone(), netdir_provider.clone(), nickname, config_rx.clone(), rend_req_tx, shutdown_rx, crate::ipt_mgr::Real { circ_pool: circ_pool.clone(), }, )?; let hs_id = { // TODO HSS BLOCKER: Look up HsId by KeyMgr based on nickname. This // is just a placeholder so the function will compile. tor_hscrypto::pk::HsId::from([0xff; 32]) }; // TODO HSS Why does this not need a keymgr? let publisher = Publisher::new( runtime, hs_id, netdir_provider, circ_pool, publisher_view, config_rx, Arc::clone(&keymgr), ); // TODO HSS: we need to actually do something with: shutdown_tx, // rend_req_rx. The latter may need to be refactored to actually work // with svc::rend_handshake, if it doesn't already. Ok(Arc::new(OnionService { inner: Mutex::new(SvcInner { config_tx, shutdown_tx, keymgr, unlaunched: Some(( rend_req_rx, Box::new(ForLaunch { publisher, ipt_mgr, ipt_mgr_view, }), )), }), })) } /// 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<(), ReconfigureError> { todo!() // TODO hss } /// Tell this onion service about some new short-term keys it can use. pub fn add_keys(&self, keys: ()) -> Result<(), Bug> { todo!() // TODO hss } /// Return the current status of this onion service. pub fn status(&self) -> OnionServiceStatus { todo!() // TODO hss } // TODO hss 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, and return a /// stream of rendezvous requests on the service. /// /// You can turn the resulting stream into a stream of [`StreamRequest`](crate::StreamRequest) /// using the [`handle_rend_requests`](crate::handle_rend_requests) helper function pub fn launch(self: &Arc) -> Result, StartupError> { let (rend_req_rx, launch) = { let mut inner = self.inner.lock().expect("poisoned lock"); inner .unlaunched .take() .ok_or(StartupError::AlreadyLaunched)? }; launch.launch()?; // TODO HSS: 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. Ok(rend_req_rx) } /// 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) { todo!() // TODO hss } }