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
224impl HasAddrs for OwnedCircTarget {
225 fn addrs(&self) -> &[SocketAddr] {
226 self.chan_target.addrs()
227 }
228}
229
230impl HasRelayIds for OwnedCircTarget {
231 fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
232 self.chan_target.identity(key_type)
233 }
234}
235impl HasChanMethod for OwnedCircTarget {
236 fn chan_method(&self) -> ChannelMethod {
237 self.chan_target.chan_method()
238 }
239}
240
241impl ChanTarget for OwnedCircTarget {}
242
243impl CircTarget for OwnedCircTarget {
244 fn ntor_onion_key(&self) -> &pk::curve25519::PublicKey {
245 &self.ntor_onion_key
246 }
247 fn protovers(&self) -> &tor_protover::Protocols {
248 &self.protocols
249 }
250}
251
252pub trait IntoOwnedChanTarget {
254 fn to_owned(self) -> OwnedChanTarget;
256
257 fn to_logged(self) -> LoggedChanTarget
259 where
260 Self: Sized,
261 {
262 self.to_owned().into()
263 }
264}
265
266impl<'a, T: ChanTarget + ?Sized> IntoOwnedChanTarget for &'a T {
267 fn to_owned(self) -> OwnedChanTarget {
268 OwnedChanTarget::from_chan_target(self)
269 }
270}
271
272impl IntoOwnedChanTarget for OwnedChanTarget {
273 fn to_owned(self) -> OwnedChanTarget {
274 self
275 }
276}
277
278pub type LoggedChanTarget = safelog::BoxSensitive<OwnedChanTarget>;
280
281#[cfg(test)]
282mod test {
283 #![allow(clippy::bool_assert_comparison)]
285 #![allow(clippy::clone_on_copy)]
286 #![allow(clippy::dbg_macro)]
287 #![allow(clippy::mixed_attributes_style)]
288 #![allow(clippy::print_stderr)]
289 #![allow(clippy::print_stdout)]
290 #![allow(clippy::single_char_pattern)]
291 #![allow(clippy::unwrap_used)]
292 #![allow(clippy::unchecked_duration_subtraction)]
293 #![allow(clippy::useless_vec)]
294 #![allow(clippy::needless_pass_by_value)]
295 use super::*;
297
298 #[test]
299 #[allow(clippy::redundant_clone)]
300 fn chan_target() {
301 let ti = OwnedChanTarget::builder()
302 .addrs(vec!["127.0.0.1:11".parse().unwrap()])
303 .ed_identity([42; 32].into())
304 .rsa_identity([45; 20].into())
305 .build()
306 .unwrap();
307
308 let ti2 = OwnedChanTarget::from_chan_target(&ti);
309 assert_eq!(ti.addrs(), ti2.addrs());
310 assert!(ti.same_relay_ids(&ti2));
311
312 assert_eq!(format!("{:?}", ti), format!("{:?}", ti2));
313 assert_eq!(format!("{:?}", ti), format!("{:?}", ti.clone()));
314 }
315
316 #[test]
317 #[allow(clippy::redundant_clone)]
318 fn circ_target() {
319 let mut builder = OwnedCircTarget::builder();
320 builder
321 .chan_target()
322 .addrs(vec!["127.0.0.1:11".parse().unwrap()])
323 .ed_identity([42; 32].into())
324 .rsa_identity([45; 20].into());
325 let ct = builder
326 .ntor_onion_key([99; 32].into())
327 .protocols("FlowCtrl=7".parse().unwrap())
328 .build()
329 .unwrap();
330 let ch = ct.chan_target.clone();
331
332 assert_eq!(ct.addrs(), ch.addrs());
333 assert!(ct.same_relay_ids(&ch));
334 assert_eq!(ct.ntor_onion_key().as_bytes(), &[99; 32]);
335 assert_eq!(&ct.protovers().to_string(), "FlowCtrl=7");
336 let ct2 = OwnedCircTarget::from_circ_target(&ct);
337 assert_eq!(format!("{:?}", ct), format!("{:?}", ct2));
338 assert_eq!(format!("{:?}", ct), format!("{:?}", ct.clone()));
339 }
340
341 #[test]
342 fn format_relay_ids() {
343 let mut builder = RelayIds::builder();
344 builder
345 .ed_identity([42; 32].into())
346 .rsa_identity([45; 20].into());
347 let ids = builder.build().unwrap();
348 assert_eq!(format!("{}", ids), "ed25519:KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKio $2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d");
349 assert_eq!(format!("{}", ids.redacted()), "ed25519:Ki…");
350 }
351}