1
//! Owned variants of [`ChanTarget`] and [`CircTarget`].
2

            
3
use safelog::Redactable;
4
use serde::{Deserialize, Serialize};
5
use std::fmt::{self, Display};
6
use std::net::SocketAddr;
7
use tor_config::impl_standard_builder;
8
use tor_llcrypto::pk;
9

            
10
use 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))]
33
pub struct RelayIds {
34
    /// Copy of the ed25519 id from the underlying ChanTarget.
35
    //
36
    // NOTE: we added serde(default) for ed25519 and rsa around April 2026.
37
    // Do not assume that they are present in older versions!
38
    #[serde(rename = "ed25519", default)]
39
    #[builder(default, setter(strip_option))]
40
    ed_identity: Option<pk::ed25519::Ed25519Identity>,
41

            
42
    /// Copy of the rsa id from the underlying ChanTarget.
43
    #[serde(rename = "rsa", default)]
44
    #[builder(default, setter(strip_option))]
45
    rsa_identity: Option<pk::rsa::RsaIdentity>,
46
}
47
impl_standard_builder! { RelayIds : !Deserialize + !Builder + !Default }
48

            
49
impl HasRelayIds for RelayIds {
50
132934957
    fn identity(&self, key_type: RelayIdType) -> Option<crate::RelayIdRef<'_>> {
51
132934957
        match key_type {
52
73791143
            RelayIdType::Ed25519 => self.ed_identity.as_ref().map(RelayIdRef::from),
53
59143814
            RelayIdType::Rsa => self.rsa_identity.as_ref().map(RelayIdRef::from),
54
        }
55
132934957
    }
56
}
57

            
58
impl RelayIds {
59
    /// Return an empty set of identities.
60
    ///
61
    /// This is _not_ a `Default` method, since this is not what you should
62
    /// usually construct.
63
5802
    pub const fn empty() -> Self {
64
5802
        Self {
65
5802
            ed_identity: None,
66
5802
            rsa_identity: None,
67
5802
        }
68
5802
    }
69

            
70
    /// Construct a new `RelayIds` object from another object that implements
71
    /// [`HasRelayIds`].
72
    ///
73
    /// Note that it is possible to construct an _empty_ `RelayIds` object if
74
    /// the input does not contain any recognized identity type.
75
3849995
    pub fn from_relay_ids<T: HasRelayIds + ?Sized>(other: &T) -> Self {
76
        Self {
77
3849995
            ed_identity: other
78
3849995
                .identity(RelayIdType::Ed25519)
79
3849995
                .map(|r| *r.unwrap_ed25519()),
80
3849995
            rsa_identity: other.identity(RelayIdType::Rsa).map(|r| *r.unwrap_rsa()),
81
        }
82
3849995
    }
83
}
84

            
85
impl std::fmt::Display for RelayIds {
86
28730
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87
28730
        write!(f, "{}", self.display_relay_ids())
88
28730
    }
89
}
90
impl Redactable for RelayIds {
91
2
    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92
2
        write!(f, "{}", self.display_relay_ids().redacted())
93
2
    }
94
}
95

            
96
impl RelayIdsBuilder {
97
    /// Construct a new `RelayIdsBuilder` object from an object implementing
98
    /// [`HasRelayIds`].
99
    ///
100
    /// Note that it is possible to construct an _empty_ `RelayIds` object if
101
    /// the input does not contain any recognized identity type.
102
75
    pub fn from_relay_ids<T: HasRelayIds + ?Sized>(ids: &T) -> Self {
103
75
        let mut builder = Self::default();
104
75
        if let Some(ed_id) = ids.ed_identity() {
105
73
            builder.ed_identity(*ed_id);
106
73
        }
107
75
        if let Some(rsa_id) = ids.rsa_identity() {
108
73
            builder.rsa_identity(*rsa_id);
109
73
        }
110
75
        builder
111
75
    }
112
}
113

            
114
/// OwnedChanTarget is a summary of a [`ChanTarget`] that owns all of its
115
/// members.
116
#[derive(Debug, Clone, derive_builder::Builder)]
117
#[builder(derive(Debug))]
118
pub struct OwnedChanTarget {
119
    /// Copy of the addresses from the underlying ChanTarget.
120
    #[builder(default)]
121
    addrs: Vec<SocketAddr>,
122
    /// Copy of the channel methods from the underlying ChanTarget.
123
    //
124
    // TODO: in many cases this will be redundant with addrs; if we allocate a
125
    // lot of these objects, we might want to handle that.
126
    #[builder(default = "self.make_method()")]
127
    method: ChannelMethod,
128
    /// Identities that this relay provides.
129
    #[builder(sub_builder)]
130
    ids: RelayIds,
131
}
132
impl_standard_builder! { OwnedChanTarget : !Deserialize + !Builder + !Default }
133

            
134
impl OwnedChanTargetBuilder {
135
    /// Set the ed25519 identity in this builder to `id`.
136
55895
    pub fn ed_identity(&mut self, id: pk::ed25519::Ed25519Identity) -> &mut Self {
137
55895
        self.ids().ed_identity(id);
138
55895
        self
139
55895
    }
140

            
141
    /// Set the RSA identity in this builder to `id`.
142
54635
    pub fn rsa_identity(&mut self, id: pk::rsa::RsaIdentity) -> &mut Self {
143
54635
        self.ids().rsa_identity(id);
144
54635
        self
145
54635
    }
146

            
147
    /// Helper: make a channel method if none was specified.
148
60175
    fn make_method(&self) -> ChannelMethod {
149
60175
        ChannelMethod::Direct(self.addrs.clone().unwrap_or_default())
150
60175
    }
151
}
152

            
153
impl HasAddrs for OwnedChanTarget {
154
203695
    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
155
203695
        self.addrs.iter().copied()
156
203695
    }
157
}
158

            
159
impl HasChanMethod for OwnedChanTarget {
160
207089
    fn chan_method(&self) -> ChannelMethod {
161
207089
        self.method.clone()
162
207089
    }
163
}
164

            
165
impl HasRelayIds for OwnedChanTarget {
166
10515685
    fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
167
10515685
        self.ids.identity(key_type)
168
10515685
    }
169
}
170

            
171
impl ChanTarget for OwnedChanTarget {}
172

            
173
impl OwnedChanTarget {
174
    /// Construct a OwnedChanTarget from a given ChanTarget.
175
2375635
    pub fn from_chan_target<C>(target: &C) -> Self
176
2375635
    where
177
2375635
        C: ChanTarget + ?Sized,
178
    {
179
2375635
        OwnedChanTarget {
180
2375635
            addrs: target.addrs().collect(),
181
2375635
            method: target.chan_method(),
182
2375635
            ids: RelayIds::from_relay_ids(target),
183
2375635
        }
184
2375635
    }
185

            
186
    /// Return a mutable reference to this [`OwnedChanTarget`]'s [`ChannelMethod`]
187
    ///
188
378
    pub fn chan_method_mut(&mut self) -> &mut ChannelMethod {
189
378
        &mut self.method
190
378
    }
191
}
192

            
193
/// Primarily for error reporting and logging
194
impl Display for OwnedChanTarget {
195
2
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
196
2
        write!(f, "{}", self.display_chan_target())
197
2
    }
198
}
199

            
200
impl Redactable for OwnedChanTarget {
201
    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202
        self.display_chan_target().display_redacted(f)
203
    }
204

            
205
    fn debug_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206
        self.display_chan_target().debug_redacted(f)
207
    }
208
}
209

            
210
/// OwnedCircTarget is a summary of a [`CircTarget`] that owns all its
211
/// members.
212
#[derive(Debug, Clone, derive_builder::Builder)]
213
#[builder(derive(Debug))]
214
pub struct OwnedCircTarget {
215
    /// The fields from this object when considered as a ChanTarget.
216
    #[builder(sub_builder)]
217
    chan_target: OwnedChanTarget,
218
    /// The ntor key to use when extending to this CircTarget
219
    ntor_onion_key: pk::curve25519::PublicKey,
220
    /// The subprotocol versions that this CircTarget supports.
221
    protocols: tor_protover::Protocols,
222
}
223
impl_standard_builder! { OwnedCircTarget : !Deserialize + !Builder + !Default }
224

            
225
impl OwnedCircTarget {
226
    /// Construct an OwnedCircTarget from a given CircTarget.
227
78650
    pub fn from_circ_target<C>(target: &C) -> Self
228
78650
    where
229
78650
        C: CircTarget + ?Sized,
230
    {
231
78650
        OwnedCircTarget {
232
78650
            chan_target: OwnedChanTarget::from_chan_target(target),
233
78650
            ntor_onion_key: *target.ntor_onion_key(),
234
78650
            protocols: target.protovers().clone(),
235
78650
        }
236
78650
    }
237

            
238
    /// Return a mutable view of this OwnedCircTarget as an [`OwnedChanTarget`].
239
    pub fn chan_target_mut(&mut self) -> &mut OwnedChanTarget {
240
        &mut self.chan_target
241
    }
242

            
243
    /// Return a  view of this OwnedCircTarget as an [`OwnedChanTarget`].
244
378
    pub fn chan_target(&self) -> &OwnedChanTarget {
245
378
        &self.chan_target
246
378
    }
247
}
248

            
249
impl HasAddrs for OwnedCircTarget {
250
5552
    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
251
5552
        self.chan_target.addrs()
252
5552
    }
253
}
254

            
255
impl HasRelayIds for OwnedCircTarget {
256
4844342
    fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
257
4844342
        self.chan_target.identity(key_type)
258
4844342
    }
259
}
260
impl HasChanMethod for OwnedCircTarget {
261
7816
    fn chan_method(&self) -> ChannelMethod {
262
7816
        self.chan_target.chan_method()
263
7816
    }
264
}
265

            
266
impl ChanTarget for OwnedCircTarget {}
267

            
268
impl CircTarget for OwnedCircTarget {
269
6686
    fn ntor_onion_key(&self) -> &pk::curve25519::PublicKey {
270
6686
        &self.ntor_onion_key
271
6686
    }
272
6686
    fn protovers(&self) -> &tor_protover::Protocols {
273
6686
        &self.protocols
274
6686
    }
275
}
276

            
277
/// A value that can be converted into an OwnedChanTarget.
278
pub trait IntoOwnedChanTarget {
279
    /// Convert this value into an [`OwnedChanTarget`].
280
    fn to_owned(self) -> OwnedChanTarget;
281

            
282
    /// Convert this value into an [`LoggedChanTarget`].
283
    fn to_logged(self) -> LoggedChanTarget
284
    where
285
        Self: Sized,
286
    {
287
        self.to_owned().into()
288
    }
289
}
290

            
291
impl<'a, T: ChanTarget + ?Sized> IntoOwnedChanTarget for &'a T {
292
    fn to_owned(self) -> OwnedChanTarget {
293
        OwnedChanTarget::from_chan_target(self)
294
    }
295
}
296

            
297
impl IntoOwnedChanTarget for OwnedChanTarget {
298
    fn to_owned(self) -> OwnedChanTarget {
299
        self
300
    }
301
}
302

            
303
/// An `OwnedChanTarget` suitable for logging and including in errors
304
pub type LoggedChanTarget = safelog::BoxSensitive<OwnedChanTarget>;
305

            
306
#[cfg(test)]
307
mod test {
308
    // @@ begin test lint list maintained by maint/add_warning @@
309
    #![allow(clippy::bool_assert_comparison)]
310
    #![allow(clippy::clone_on_copy)]
311
    #![allow(clippy::dbg_macro)]
312
    #![allow(clippy::mixed_attributes_style)]
313
    #![allow(clippy::print_stderr)]
314
    #![allow(clippy::print_stdout)]
315
    #![allow(clippy::single_char_pattern)]
316
    #![allow(clippy::unwrap_used)]
317
    #![allow(clippy::unchecked_time_subtraction)]
318
    #![allow(clippy::useless_vec)]
319
    #![allow(clippy::needless_pass_by_value)]
320
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
321
    use super::*;
322
    use itertools::Itertools;
323

            
324
    #[test]
325
    #[allow(clippy::redundant_clone)]
326
    fn chan_target() {
327
        let ti = OwnedChanTarget::builder()
328
            .addrs(vec!["127.0.0.1:11".parse().unwrap()])
329
            .ed_identity([42; 32].into())
330
            .rsa_identity([45; 20].into())
331
            .build()
332
            .unwrap();
333

            
334
        let ti2 = OwnedChanTarget::from_chan_target(&ti);
335
        assert_eq!(ti.addrs().collect_vec(), ti2.addrs().collect_vec());
336
        assert!(ti.same_relay_ids(&ti2));
337

            
338
        assert_eq!(format!("{:?}", ti), format!("{:?}", ti2));
339
        assert_eq!(format!("{:?}", ti), format!("{:?}", ti.clone()));
340
    }
341

            
342
    #[test]
343
    #[allow(clippy::redundant_clone)]
344
    fn circ_target() {
345
        let mut builder = OwnedCircTarget::builder();
346
        builder
347
            .chan_target()
348
            .addrs(vec!["127.0.0.1:11".parse().unwrap()])
349
            .ed_identity([42; 32].into())
350
            .rsa_identity([45; 20].into());
351
        let ct = builder
352
            .ntor_onion_key([99; 32].into())
353
            .protocols("FlowCtrl=7".parse().unwrap())
354
            .build()
355
            .unwrap();
356
        let ch = ct.chan_target.clone();
357

            
358
        assert_eq!(ct.addrs().collect_vec(), ch.addrs().collect_vec());
359
        assert!(ct.same_relay_ids(&ch));
360
        assert_eq!(ct.ntor_onion_key().as_bytes(), &[99; 32]);
361
        assert_eq!(&ct.protovers().to_string(), "FlowCtrl=7");
362
        let ct2 = OwnedCircTarget::from_circ_target(&ct);
363
        assert_eq!(format!("{:?}", ct), format!("{:?}", ct2));
364
        assert_eq!(format!("{:?}", ct), format!("{:?}", ct.clone()));
365
    }
366

            
367
    #[test]
368
    fn format_relay_ids() {
369
        let mut builder = RelayIds::builder();
370
        builder
371
            .ed_identity([42; 32].into())
372
            .rsa_identity([45; 20].into());
373
        let ids = builder.build().unwrap();
374
        assert_eq!(
375
            format!("{}", ids),
376
            "ed25519:KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKio $2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d"
377
        );
378
        assert_eq!(format!("{}", ids.redacted()), "ed25519:Ki…");
379
    }
380
}