1use 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#[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 #[serde(rename = "ed25519")]
36 #[builder(default, setter(strip_option))]
37 ed_identity: Option<pk::ed25519::Ed25519Identity>,
38 #[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 pub const fn empty() -> Self {
60 Self {
61 ed_identity: None,
62 rsa_identity: None,
63 }
64 }
65
66 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
92#[derive(Debug, Clone, derive_builder::Builder)]
95#[builder(derive(Debug))]
96pub struct OwnedChanTarget {
97 #[builder(default)]
99 addrs: Vec<SocketAddr>,
100 #[builder(default = "self.make_method()")]
105 method: ChannelMethod,
106 #[builder(sub_builder)]
108 ids: RelayIds,
109}
110impl_standard_builder! { OwnedChanTarget : !Deserialize + !Builder + !Default }
111
112impl OwnedChanTargetBuilder {
113 pub fn ed_identity(&mut self, id: pk::ed25519::Ed25519Identity) -> &mut Self {
115 self.ids().ed_identity(id);
116 self
117 }
118
119 pub fn rsa_identity(&mut self, id: pk::rsa::RsaIdentity) -> &mut Self {
121 self.ids().rsa_identity(id);
122 self
123 }
124
125 fn make_method(&self) -> ChannelMethod {
127 ChannelMethod::Direct(self.addrs.clone().unwrap_or_default())
128 }
129}
130
131impl HasAddrs for OwnedChanTarget {
132 fn addrs(&self) -> &[SocketAddr] {
133 &self.addrs[..]
134 }
135}
136
137impl HasChanMethod for OwnedChanTarget {
138 fn chan_method(&self) -> ChannelMethod {
139 self.method.clone()
140 }
141}
142
143impl HasRelayIds for OwnedChanTarget {
144 fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
145 self.ids.identity(key_type)
146 }
147}
148
149impl ChanTarget for OwnedChanTarget {}
150
151impl OwnedChanTarget {
152 pub fn from_chan_target<C>(target: &C) -> Self
154 where
155 C: ChanTarget + ?Sized,
156 {
157 OwnedChanTarget {
158 addrs: target.addrs().to_vec(),
159 method: target.chan_method(),
160 ids: RelayIds::from_relay_ids(target),
161 }
162 }
163
164 pub fn chan_method_mut(&mut self) -> &mut ChannelMethod {
167 &mut self.method
168 }
169}
170
171impl Display for OwnedChanTarget {
173 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
174 write!(f, "{}", self.display_chan_target())
175 }
176}
177
178impl Redactable for OwnedChanTarget {
179 fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 self.display_chan_target().display_redacted(f)
181 }
182
183 fn debug_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 self.display_chan_target().debug_redacted(f)
185 }
186}
187
188#[derive(Debug, Clone, derive_builder::Builder)]
191#[builder(derive(Debug))]
192pub struct OwnedCircTarget {
193 #[builder(sub_builder)]
195 chan_target: OwnedChanTarget,
196 ntor_onion_key: pk::curve25519::PublicKey,
198 protocols: tor_protover::Protocols,
200}
201impl_standard_builder! { OwnedCircTarget : !Deserialize + !Builder + !Default }
202
203impl OwnedCircTarget {
204 pub fn from_circ_target<C>(target: &C) -> Self
206 where
207 C: CircTarget + ?Sized,
208 {
209 OwnedCircTarget {
210 chan_target: OwnedChanTarget::from_chan_target(target),
211 ntor_onion_key: *target.ntor_onion_key(),
212 protocols: target.protovers().clone(),
215 }
216 }
217
218 pub fn chan_target_mut(&mut self) -> &mut OwnedChanTarget {
220 &mut self.chan_target
221 }
222
223 pub fn chan_target(&self) -> &OwnedChanTarget {
225 &self.chan_target
226 }
227}
228
229impl HasAddrs for OwnedCircTarget {
230 fn addrs(&self) -> &[SocketAddr] {
231 self.chan_target.addrs()
232 }
233}
234
235impl HasRelayIds for OwnedCircTarget {
236 fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
237 self.chan_target.identity(key_type)
238 }
239}
240impl HasChanMethod for OwnedCircTarget {
241 fn chan_method(&self) -> ChannelMethod {
242 self.chan_target.chan_method()
243 }
244}
245
246impl ChanTarget for OwnedCircTarget {}
247
248impl CircTarget for OwnedCircTarget {
249 fn ntor_onion_key(&self) -> &pk::curve25519::PublicKey {
250 &self.ntor_onion_key
251 }
252 fn protovers(&self) -> &tor_protover::Protocols {
253 &self.protocols
254 }
255}
256
257pub trait IntoOwnedChanTarget {
259 fn to_owned(self) -> OwnedChanTarget;
261
262 fn to_logged(self) -> LoggedChanTarget
264 where
265 Self: Sized,
266 {
267 self.to_owned().into()
268 }
269}
270
271impl<'a, T: ChanTarget + ?Sized> IntoOwnedChanTarget for &'a T {
272 fn to_owned(self) -> OwnedChanTarget {
273 OwnedChanTarget::from_chan_target(self)
274 }
275}
276
277impl IntoOwnedChanTarget for OwnedChanTarget {
278 fn to_owned(self) -> OwnedChanTarget {
279 self
280 }
281}
282
283pub type LoggedChanTarget = safelog::BoxSensitive<OwnedChanTarget>;
285
286#[cfg(test)]
287mod test {
288 #![allow(clippy::bool_assert_comparison)]
290 #![allow(clippy::clone_on_copy)]
291 #![allow(clippy::dbg_macro)]
292 #![allow(clippy::mixed_attributes_style)]
293 #![allow(clippy::print_stderr)]
294 #![allow(clippy::print_stdout)]
295 #![allow(clippy::single_char_pattern)]
296 #![allow(clippy::unwrap_used)]
297 #![allow(clippy::unchecked_duration_subtraction)]
298 #![allow(clippy::useless_vec)]
299 #![allow(clippy::needless_pass_by_value)]
300 use super::*;
302
303 #[test]
304 #[allow(clippy::redundant_clone)]
305 fn chan_target() {
306 let ti = OwnedChanTarget::builder()
307 .addrs(vec!["127.0.0.1:11".parse().unwrap()])
308 .ed_identity([42; 32].into())
309 .rsa_identity([45; 20].into())
310 .build()
311 .unwrap();
312
313 let ti2 = OwnedChanTarget::from_chan_target(&ti);
314 assert_eq!(ti.addrs(), ti2.addrs());
315 assert!(ti.same_relay_ids(&ti2));
316
317 assert_eq!(format!("{:?}", ti), format!("{:?}", ti2));
318 assert_eq!(format!("{:?}", ti), format!("{:?}", ti.clone()));
319 }
320
321 #[test]
322 #[allow(clippy::redundant_clone)]
323 fn circ_target() {
324 let mut builder = OwnedCircTarget::builder();
325 builder
326 .chan_target()
327 .addrs(vec!["127.0.0.1:11".parse().unwrap()])
328 .ed_identity([42; 32].into())
329 .rsa_identity([45; 20].into());
330 let ct = builder
331 .ntor_onion_key([99; 32].into())
332 .protocols("FlowCtrl=7".parse().unwrap())
333 .build()
334 .unwrap();
335 let ch = ct.chan_target.clone();
336
337 assert_eq!(ct.addrs(), ch.addrs());
338 assert!(ct.same_relay_ids(&ch));
339 assert_eq!(ct.ntor_onion_key().as_bytes(), &[99; 32]);
340 assert_eq!(&ct.protovers().to_string(), "FlowCtrl=7");
341 let ct2 = OwnedCircTarget::from_circ_target(&ct);
342 assert_eq!(format!("{:?}", ct), format!("{:?}", ct2));
343 assert_eq!(format!("{:?}", ct), format!("{:?}", ct.clone()));
344 }
345
346 #[test]
347 fn format_relay_ids() {
348 let mut builder = RelayIds::builder();
349 builder
350 .ed_identity([42; 32].into())
351 .rsa_identity([45; 20].into());
352 let ids = builder.build().unwrap();
353 assert_eq!(
354 format!("{}", ids),
355 "ed25519:KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKio $2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d"
356 );
357 assert_eq!(format!("{}", ids.redacted()), "ed25519:Ki…");
358 }
359}