Skip to main content

tor_linkspec/
owned.rs

1//! Owned variants of [`ChanTarget`] and [`CircTarget`].
2
3use safelog::Redactable;
4use serde::{Deserialize, Serialize};
5use std::fmt::{self, Display};
6use std::net::SocketAddr;
7use tor_config::impl_standard_builder;
8use tor_llcrypto::pk;
9
10use crate::{
11    ChanTarget, ChannelMethod, CircTarget, HasAddrs, HasChanMethod, HasRelayIds, RelayIdRef,
12    RelayIdType,
13};
14
15/// RelayIds is an owned copy of the set of known identities of a relay.
16///
17/// Note that an object of this type will not necessarily have every type of
18/// identity: it's possible that we don't know all the identities, or that one
19/// of the identity types has become optional.
20#[derive(
21    Debug,
22    Clone,
23    Eq,
24    PartialEq,
25    Hash,
26    PartialOrd,
27    Ord,
28    Serialize,
29    Deserialize,
30    derive_builder::Builder,
31)]
32#[builder(derive(Debug))]
33pub struct RelayIds {
34    /// Copy of the ed25519 id from the underlying ChanTarget.
35    #[serde(rename = "ed25519")]
36    #[builder(default, setter(strip_option))]
37    ed_identity: Option<pk::ed25519::Ed25519Identity>,
38    /// Copy of the rsa id from the underlying ChanTarget.
39    #[serde(rename = "rsa")]
40    #[builder(default, setter(strip_option))]
41    rsa_identity: Option<pk::rsa::RsaIdentity>,
42}
43impl_standard_builder! { RelayIds : !Deserialize + !Builder + !Default }
44
45impl HasRelayIds for RelayIds {
46    fn identity(&self, key_type: RelayIdType) -> Option<crate::RelayIdRef<'_>> {
47        match key_type {
48            RelayIdType::Ed25519 => self.ed_identity.as_ref().map(RelayIdRef::from),
49            RelayIdType::Rsa => self.rsa_identity.as_ref().map(RelayIdRef::from),
50        }
51    }
52}
53
54impl RelayIds {
55    /// Return an empty set of identities.
56    ///
57    /// This is _not_ a `Default` method, since this is not what you should
58    /// usually construct.
59    pub const fn empty() -> Self {
60        Self {
61            ed_identity: None,
62            rsa_identity: None,
63        }
64    }
65
66    /// Construct a new `RelayIds` object from another object that implements
67    /// [`HasRelayIds`].
68    ///
69    /// Note that it is possible to construct an _empty_ `RelayIds` object if
70    /// the input does not contain any recognized identity type.
71    pub fn from_relay_ids<T: HasRelayIds + ?Sized>(other: &T) -> Self {
72        Self {
73            ed_identity: other
74                .identity(RelayIdType::Ed25519)
75                .map(|r| *r.unwrap_ed25519()),
76            rsa_identity: other.identity(RelayIdType::Rsa).map(|r| *r.unwrap_rsa()),
77        }
78    }
79}
80
81impl std::fmt::Display for RelayIds {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        write!(f, "{}", self.display_relay_ids())
84    }
85}
86impl Redactable for RelayIds {
87    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        write!(f, "{}", self.display_relay_ids().redacted())
89    }
90}
91
92impl RelayIdsBuilder {
93    /// Construct a new `RelayIdsBuilder` object from an object implementing
94    /// [`HasRelayIds`].
95    ///
96    /// Note that it is possible to construct an _empty_ `RelayIds` object if
97    /// the input does not contain any recognized identity type.
98    pub fn from_relay_ids<T: HasRelayIds + ?Sized>(ids: &T) -> Self {
99        let mut builder = Self::default();
100        if let Some(ed_id) = ids.ed_identity() {
101            builder.ed_identity(*ed_id);
102        }
103        if let Some(rsa_id) = ids.rsa_identity() {
104            builder.rsa_identity(*rsa_id);
105        }
106        builder
107    }
108}
109
110/// OwnedChanTarget is a summary of a [`ChanTarget`] that owns all of its
111/// members.
112#[derive(Debug, Clone, derive_builder::Builder)]
113#[builder(derive(Debug))]
114pub struct OwnedChanTarget {
115    /// Copy of the addresses from the underlying ChanTarget.
116    #[builder(default)]
117    addrs: Vec<SocketAddr>,
118    /// Copy of the channel methods from the underlying ChanTarget.
119    //
120    // TODO: in many cases this will be redundant with addrs; if we allocate a
121    // lot of these objects, we might want to handle that.
122    #[builder(default = "self.make_method()")]
123    method: ChannelMethod,
124    /// Identities that this relay provides.
125    #[builder(sub_builder)]
126    ids: RelayIds,
127}
128impl_standard_builder! { OwnedChanTarget : !Deserialize + !Builder + !Default }
129
130impl OwnedChanTargetBuilder {
131    /// Set the ed25519 identity in this builder to `id`.
132    pub fn ed_identity(&mut self, id: pk::ed25519::Ed25519Identity) -> &mut Self {
133        self.ids().ed_identity(id);
134        self
135    }
136
137    /// Set the RSA identity in this builder to `id`.
138    pub fn rsa_identity(&mut self, id: pk::rsa::RsaIdentity) -> &mut Self {
139        self.ids().rsa_identity(id);
140        self
141    }
142
143    /// Helper: make a channel method if none was specified.
144    fn make_method(&self) -> ChannelMethod {
145        ChannelMethod::Direct(self.addrs.clone().unwrap_or_default())
146    }
147}
148
149impl HasAddrs for OwnedChanTarget {
150    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
151        self.addrs.iter().copied()
152    }
153}
154
155impl HasChanMethod for OwnedChanTarget {
156    fn chan_method(&self) -> ChannelMethod {
157        self.method.clone()
158    }
159}
160
161impl HasRelayIds for OwnedChanTarget {
162    fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
163        self.ids.identity(key_type)
164    }
165}
166
167impl ChanTarget for OwnedChanTarget {}
168
169impl OwnedChanTarget {
170    /// Construct a OwnedChanTarget from a given ChanTarget.
171    pub fn from_chan_target<C>(target: &C) -> Self
172    where
173        C: ChanTarget + ?Sized,
174    {
175        OwnedChanTarget {
176            addrs: target.addrs().collect(),
177            method: target.chan_method(),
178            ids: RelayIds::from_relay_ids(target),
179        }
180    }
181
182    /// Return a mutable reference to this [`OwnedChanTarget`]'s [`ChannelMethod`]
183    ///
184    pub fn chan_method_mut(&mut self) -> &mut ChannelMethod {
185        &mut self.method
186    }
187}
188
189/// Primarily for error reporting and logging
190impl Display for OwnedChanTarget {
191    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192        write!(f, "{}", self.display_chan_target())
193    }
194}
195
196impl Redactable for OwnedChanTarget {
197    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198        self.display_chan_target().display_redacted(f)
199    }
200
201    fn debug_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202        self.display_chan_target().debug_redacted(f)
203    }
204}
205
206/// OwnedCircTarget is a summary of a [`CircTarget`] that owns all its
207/// members.
208#[derive(Debug, Clone, derive_builder::Builder)]
209#[builder(derive(Debug))]
210pub struct OwnedCircTarget {
211    /// The fields from this object when considered as a ChanTarget.
212    #[builder(sub_builder)]
213    chan_target: OwnedChanTarget,
214    /// The ntor key to use when extending to this CircTarget
215    ntor_onion_key: pk::curve25519::PublicKey,
216    /// The subprotocol versions that this CircTarget supports.
217    protocols: tor_protover::Protocols,
218}
219impl_standard_builder! { OwnedCircTarget : !Deserialize + !Builder + !Default }
220
221impl OwnedCircTarget {
222    /// Construct an OwnedCircTarget from a given CircTarget.
223    pub fn from_circ_target<C>(target: &C) -> Self
224    where
225        C: CircTarget + ?Sized,
226    {
227        OwnedCircTarget {
228            chan_target: OwnedChanTarget::from_chan_target(target),
229            ntor_onion_key: *target.ntor_onion_key(),
230            protocols: target.protovers().clone(),
231        }
232    }
233
234    /// Return a mutable view of this OwnedCircTarget as an [`OwnedChanTarget`].
235    pub fn chan_target_mut(&mut self) -> &mut OwnedChanTarget {
236        &mut self.chan_target
237    }
238
239    /// Return a  view of this OwnedCircTarget as an [`OwnedChanTarget`].
240    pub fn chan_target(&self) -> &OwnedChanTarget {
241        &self.chan_target
242    }
243}
244
245impl HasAddrs for OwnedCircTarget {
246    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
247        self.chan_target.addrs()
248    }
249}
250
251impl HasRelayIds for OwnedCircTarget {
252    fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
253        self.chan_target.identity(key_type)
254    }
255}
256impl HasChanMethod for OwnedCircTarget {
257    fn chan_method(&self) -> ChannelMethod {
258        self.chan_target.chan_method()
259    }
260}
261
262impl ChanTarget for OwnedCircTarget {}
263
264impl CircTarget for OwnedCircTarget {
265    fn ntor_onion_key(&self) -> &pk::curve25519::PublicKey {
266        &self.ntor_onion_key
267    }
268    fn protovers(&self) -> &tor_protover::Protocols {
269        &self.protocols
270    }
271}
272
273/// A value that can be converted into an OwnedChanTarget.
274pub trait IntoOwnedChanTarget {
275    /// Convert this value into an [`OwnedChanTarget`].
276    fn to_owned(self) -> OwnedChanTarget;
277
278    /// Convert this value into an [`LoggedChanTarget`].
279    fn to_logged(self) -> LoggedChanTarget
280    where
281        Self: Sized,
282    {
283        self.to_owned().into()
284    }
285}
286
287impl<'a, T: ChanTarget + ?Sized> IntoOwnedChanTarget for &'a T {
288    fn to_owned(self) -> OwnedChanTarget {
289        OwnedChanTarget::from_chan_target(self)
290    }
291}
292
293impl IntoOwnedChanTarget for OwnedChanTarget {
294    fn to_owned(self) -> OwnedChanTarget {
295        self
296    }
297}
298
299/// An `OwnedChanTarget` suitable for logging and including in errors
300pub type LoggedChanTarget = safelog::BoxSensitive<OwnedChanTarget>;
301
302#[cfg(test)]
303mod test {
304    // @@ begin test lint list maintained by maint/add_warning @@
305    #![allow(clippy::bool_assert_comparison)]
306    #![allow(clippy::clone_on_copy)]
307    #![allow(clippy::dbg_macro)]
308    #![allow(clippy::mixed_attributes_style)]
309    #![allow(clippy::print_stderr)]
310    #![allow(clippy::print_stdout)]
311    #![allow(clippy::single_char_pattern)]
312    #![allow(clippy::unwrap_used)]
313    #![allow(clippy::unchecked_time_subtraction)]
314    #![allow(clippy::useless_vec)]
315    #![allow(clippy::needless_pass_by_value)]
316    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
317    use super::*;
318    use itertools::Itertools;
319
320    #[test]
321    #[allow(clippy::redundant_clone)]
322    fn chan_target() {
323        let ti = OwnedChanTarget::builder()
324            .addrs(vec!["127.0.0.1:11".parse().unwrap()])
325            .ed_identity([42; 32].into())
326            .rsa_identity([45; 20].into())
327            .build()
328            .unwrap();
329
330        let ti2 = OwnedChanTarget::from_chan_target(&ti);
331        assert_eq!(ti.addrs().collect_vec(), ti2.addrs().collect_vec());
332        assert!(ti.same_relay_ids(&ti2));
333
334        assert_eq!(format!("{:?}", ti), format!("{:?}", ti2));
335        assert_eq!(format!("{:?}", ti), format!("{:?}", ti.clone()));
336    }
337
338    #[test]
339    #[allow(clippy::redundant_clone)]
340    fn circ_target() {
341        let mut builder = OwnedCircTarget::builder();
342        builder
343            .chan_target()
344            .addrs(vec!["127.0.0.1:11".parse().unwrap()])
345            .ed_identity([42; 32].into())
346            .rsa_identity([45; 20].into());
347        let ct = builder
348            .ntor_onion_key([99; 32].into())
349            .protocols("FlowCtrl=7".parse().unwrap())
350            .build()
351            .unwrap();
352        let ch = ct.chan_target.clone();
353
354        assert_eq!(ct.addrs().collect_vec(), ch.addrs().collect_vec());
355        assert!(ct.same_relay_ids(&ch));
356        assert_eq!(ct.ntor_onion_key().as_bytes(), &[99; 32]);
357        assert_eq!(&ct.protovers().to_string(), "FlowCtrl=7");
358        let ct2 = OwnedCircTarget::from_circ_target(&ct);
359        assert_eq!(format!("{:?}", ct), format!("{:?}", ct2));
360        assert_eq!(format!("{:?}", ct), format!("{:?}", ct.clone()));
361    }
362
363    #[test]
364    fn format_relay_ids() {
365        let mut builder = RelayIds::builder();
366        builder
367            .ed_identity([42; 32].into())
368            .rsa_identity([45; 20].into());
369        let ids = builder.build().unwrap();
370        assert_eq!(
371            format!("{}", ids),
372            "ed25519:KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKio $2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d"
373        );
374        assert_eq!(format!("{}", ids.redacted()), "ed25519:Ki…");
375    }
376}