#![allow(clippy::unwrap_used)]
use futures::channel::mpsc;
use futures::{SinkExt as _, StreamExt as _};
use safelog::MaybeSensitive;
use std::sync::{Arc, LazyLock, Weak};
use std::time::Duration;
use tor_cell::chancell::AnyChanCell;
use tor_key_forge::Keygen as _;
use tor_linkspec::{
HasRelayIds as _, OwnedChanTarget, OwnedCircTarget, OwnedCircTargetBuilder, RelayIds,
RelayIdsBuilder,
};
use tor_rtcompat::{NoOpStreamOpsHandle, Runtime, SpawnExt as _};
use crate::ClockSkew;
use crate::channel::circmap::CircIdRange;
use crate::channel::{
BoxedChannelSink, BoxedChannelStream, Canonicity, Channel, ChannelMode, Reactor, UniqId,
};
use crate::client::circuit::{PendingClientTunnel, TimeoutEstimator};
use crate::memquota::{ChannelAccount, SpecificAccount};
use crate::peer::{PeerAddr, PeerInfo};
#[cfg(feature = "relay")]
use {
crate::relay::CreateRequestHandler,
crate::relay::channel_provider::{ChannelProvider, NoOpChannelProvider},
crate::relay::{CircNetParameters, CircuitIncomingStreamReceiver, CongestionControlNetParams},
crate::stream::incoming::NoOpRequestFilter,
tor_relay_crypto::pk::RelayNtorKeys,
};
pub(crate) fn new_channel<R: Runtime>(
rt: &R,
mode: ChannelMode,
peer_info: PeerInfo,
sender: BoxedChannelSink,
receiver: BoxedChannelStream,
) -> (Arc<Channel>, Reactor<R>) {
let mut peer_id = OwnedChanTarget::builder();
*peer_id.ids() = RelayIdsBuilder::from_relay_ids(&peer_info);
let peer_id = peer_id.build().unwrap();
let link_protocol = 4;
let clock_skew = ClockSkew::None;
let canonicity = Canonicity {
peer_is_canonical: true,
canonical_to_peer: true,
};
let memquota = ChannelAccount::new_noop();
let (chan, reactor) = Channel::new(
mode,
link_protocol,
sender,
receiver,
Box::new(NoOpStreamOpsHandle::default()),
UniqId::new(),
peer_id,
MaybeSensitive::not_sensitive(peer_info),
clock_skew,
rt.clone(),
memquota,
canonicity,
)
.unwrap();
(chan, reactor)
}
#[cfg(feature = "relay")]
pub(crate) fn new_channel_pair<R: Runtime>(
rt: &R,
chan_provider: Weak<dyn ChannelProvider<BuildSpec = OwnedChanTarget> + Send + Sync>,
relay_ids: RelayIds,
relay_ntor_keys: RelayNtorKeys,
conn_inspector: &ConnInspector,
) -> (Arc<Channel>, Arc<Channel>, CircuitIncomingStreamReceiver) {
let relay_info = PeerInfo::new(PeerAddr::UNSPECIFIED, relay_ids);
let client_info = PeerInfo::new(PeerAddr::UNSPECIFIED, RelayIds::empty());
let circ_net_params = CircNetParameters {
cc: CongestionControlNetParams::defaults_for_tests(),
};
let (create_request_handler, circuit_stream_rx) = CreateRequestHandler::new(
chan_provider,
circ_net_params,
relay_ntor_keys,
Box::new(|| Box::new(NoOpRequestFilter) as Box<_>),
&[],
);
let create_request_handler = Arc::new(create_request_handler);
let client_mode = ChannelMode::Client;
let relay_mode = ChannelMode::Relay {
create_request_handler,
our_ed25519_id: *relay_info.ed_identity().unwrap(),
our_rsa_id: *relay_info.rsa_identity().unwrap(),
circ_id_range: CircIdRange::Low,
};
let c_to_r_tx: mpsc::Sender<AnyChanCell>;
let r_to_c_tx: mpsc::Sender<AnyChanCell>;
let c_to_r_rx: mpsc::Receiver<AnyChanCell>;
let r_to_c_rx: mpsc::Receiver<AnyChanCell>;
(c_to_r_tx, c_to_r_rx) = mpsc::channel(32);
(r_to_c_tx, r_to_c_rx) = mpsc::channel(32);
let c_to_r_rx = c_to_r_rx.map(Ok);
let r_to_c_rx = r_to_c_rx.map(Ok);
let c_to_r_tx = c_to_r_tx.sink_map_err(|e| crate::Error::CellDecodeErr {
object: "reactor test",
err: tor_cell::Error::ChanProto(format!("Sink error: {e:?}")),
});
let r_to_c_tx = r_to_c_tx.sink_map_err(|e| crate::Error::CellDecodeErr {
object: "reactor test",
err: tor_cell::Error::ChanProto(format!("Sink error: {e:?}")),
});
let client_inspector_tx = conn_inspector.client_inspector_tx.clone();
let c_to_r_tx = c_to_r_tx.with(move |cell: AnyChanCell| {
let client_inspector_tx = client_inspector_tx.clone();
async move {
let (cell, cell_clone) = clone_chan_cell(cell);
let _ = client_inspector_tx.unbounded_send(cell_clone);
Ok(cell)
}
});
let c_to_r_tx = Box::pin(c_to_r_tx);
let relay_inspector_tx = conn_inspector.relay_inspector_tx.clone();
let r_to_c_tx = r_to_c_tx.with(move |cell: AnyChanCell| {
let relay_inspector_tx = relay_inspector_tx.clone();
async move {
let (cell, cell_clone) = clone_chan_cell(cell);
let _ = relay_inspector_tx.unbounded_send(cell_clone);
Ok(cell)
}
});
let r_to_c_tx = Box::pin(r_to_c_tx);
let (c_to_r_tx, c_to_r_rx) = (Box::new(c_to_r_tx), Box::new(c_to_r_rx));
let (r_to_c_tx, r_to_c_rx) = (Box::new(r_to_c_tx), Box::new(r_to_c_rx));
let (client_chan, client_reactor) =
new_channel(rt, client_mode, relay_info, c_to_r_tx, r_to_c_rx);
let (relay_chan, relay_reactor) =
new_channel(rt, relay_mode, client_info, r_to_c_tx, c_to_r_rx);
rt.spawn(async {
let _ = futures::future::join(client_reactor.run(), relay_reactor.run()).await;
})
.unwrap();
(client_chan, relay_chan, circuit_stream_rx)
}
#[cfg(feature = "relay")]
pub(crate) fn new_channel_pair_with_keys<R: Runtime>(
rt: &R,
conn_inspector: &ConnInspector,
) -> (
Arc<Channel>,
Arc<Channel>,
CircuitIncomingStreamReceiver,
OwnedCircTargetBuilder,
) {
let mut rng = tor_llcrypto::rng::CautiousRng;
let relay_ids = RelayIds::builder()
.ed_identity([6_u8; 32].into())
.rsa_identity([10_u8; 20].into())
.build()
.unwrap();
let relay_ntor_keys = tor_llcrypto::pk::curve25519::StaticKeypair::generate(&mut rng).unwrap();
let relay_ntor_keys = RelayNtorKeys::new(relay_ntor_keys.into());
static CHAN_PROVIDER: LazyLock<Arc<NoOpChannelProvider>> =
LazyLock::new(|| Arc::new(NoOpChannelProvider));
let chan_provider = Arc::downgrade(&CHAN_PROVIDER);
let (client_chan, relay_chan, circuit_stream_rx) = new_channel_pair(
rt,
chan_provider,
relay_ids.clone(),
relay_ntor_keys.clone(),
conn_inspector,
);
let mut target_builder = OwnedCircTarget::builder();
target_builder.ntor_onion_key(*relay_ntor_keys.latest().public().inner());
*target_builder.chan_target().ids() = RelayIdsBuilder::from_relay_ids(&relay_ids);
(client_chan, relay_chan, circuit_stream_rx, target_builder)
}
pub(crate) async fn new_pending_tunnel<R: Runtime>(
rt: &R,
channel: &Arc<Channel>,
) -> PendingClientTunnel {
struct Timeouts;
impl TimeoutEstimator for Timeouts {
fn circuit_build_timeout(&self, _length: usize) -> Duration {
Duration::from_secs(60)
}
}
let (pending_tunnel, reactor) = channel.new_tunnel(Arc::new(Timeouts)).await.unwrap();
rt.spawn(async {
let _ = reactor.run().await;
})
.unwrap();
pending_tunnel
}
#[cfg(feature = "relay")]
fn clone_chan_cell(cell: AnyChanCell) -> (AnyChanCell, AnyChanCell) {
let (circ_id, msg) = cell.into_circid_and_msg();
let cell_1 = AnyChanCell::new(circ_id, msg.clone());
let cell_2 = AnyChanCell::new(circ_id, msg);
(cell_1, cell_2)
}
#[cfg(feature = "relay")]
pub(crate) struct ConnInspector {
client_inspector_tx: mpsc::UnboundedSender<AnyChanCell>,
client_inspector_rx: mpsc::UnboundedReceiver<AnyChanCell>,
relay_inspector_tx: mpsc::UnboundedSender<AnyChanCell>,
relay_inspector_rx: mpsc::UnboundedReceiver<AnyChanCell>,
}
#[cfg(feature = "relay")]
impl ConnInspector {
pub(crate) fn new() -> Self {
let (client_inspector_tx, client_inspector_rx) = mpsc::unbounded();
let (relay_inspector_tx, relay_inspector_rx) = mpsc::unbounded();
ConnInspector {
client_inspector_tx,
client_inspector_rx,
relay_inspector_tx,
relay_inspector_rx,
}
}
pub(crate) fn try_client_cell(&mut self) -> Option<AnyChanCell> {
self.client_inspector_rx.try_recv().ok()
}
pub(crate) fn try_relay_cell(&mut self) -> Option<AnyChanCell> {
self.relay_inspector_rx.try_recv().ok()
}
pub(crate) async fn client_cell(&mut self) -> Option<AnyChanCell> {
self.client_inspector_rx.recv().await.ok()
}
pub(crate) async fn relay_cell(&mut self) -> Option<AnyChanCell> {
self.relay_inspector_rx.recv().await.ok()
}
}