1use derive_deftly::derive_deftly_adhoc;
5use itertools::Itertools;
6use safelog::Redactable;
7use std::{
8 fmt,
9 iter::FusedIterator,
10 net::{IpAddr, SocketAddr},
11};
12use tor_llcrypto::pk;
13
14use crate::{ChannelMethod, RelayIdRef, RelayIdType, RelayIdTypeIter};
15
16#[cfg(feature = "pt-client")]
17use crate::PtTargetAddr;
18
19pub trait HasRelayIdsLegacy {
24 fn ed_identity(&self) -> &pk::ed25519::Ed25519Identity;
26 fn rsa_identity(&self) -> &pk::rsa::RsaIdentity;
28}
29
30pub trait HasRelayIds {
36 fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>>;
42
43 fn identities(&self) -> RelayIdIter<'_, Self> {
45 RelayIdIter {
46 info: self,
47 next_key: RelayIdType::all_types(),
48 }
49 }
50
51 fn ed_identity(&self) -> Option<&pk::ed25519::Ed25519Identity> {
53 self.identity(RelayIdType::Ed25519)
54 .map(RelayIdRef::unwrap_ed25519)
55 }
56
57 fn rsa_identity(&self) -> Option<&pk::rsa::RsaIdentity> {
59 self.identity(RelayIdType::Rsa).map(RelayIdRef::unwrap_rsa)
60 }
61
62 fn has_identity(&self, id: RelayIdRef<'_>) -> bool {
71 self.identity(id.id_type()).map(|my_id| my_id == id) == Some(true)
72 }
73
74 fn has_any_identity(&self) -> bool {
76 RelayIdType::all_types().any(|id_type| self.identity(id_type).is_some())
77 }
78
79 #[allow(clippy::nonminimal_bool)] fn same_relay_ids<T: HasRelayIds + ?Sized>(&self, other: &T) -> bool {
91 derive_deftly_adhoc! {
122 RelayIdType:
123 $(
124 self.identity($vtype) == other.identity($vtype) &&
125 )
126 true
127 }
128 }
129
130 fn has_all_relay_ids_from<T: HasRelayIds + ?Sized>(&self, other: &T) -> bool {
135 RelayIdType::all_types().all(|key_type| {
136 match (self.identity(key_type), other.identity(key_type)) {
137 (Some(mine), Some(theirs)) if mine == theirs => true,
139 (_, Some(_theirs)) => false,
141 (_, None) => true,
143 }
144 })
145 }
146
147 fn has_any_relay_id_from<T: HasRelayIds + ?Sized>(&self, other: &T) -> bool {
152 RelayIdType::all_types()
153 .filter_map(|key_type| Some((self.identity(key_type)?, other.identity(key_type)?)))
154 .any(|(self_id, other_id)| self_id == other_id)
155 }
156
157 fn cmp_by_relay_ids<T: HasRelayIds + ?Sized>(&self, other: &T) -> std::cmp::Ordering {
166 for key_type in RelayIdType::all_types() {
167 let ordering = Ord::cmp(&self.identity(key_type), &other.identity(key_type));
168 if ordering.is_ne() {
169 return ordering;
170 }
171 }
172 std::cmp::Ordering::Equal
173 }
174
175 fn display_relay_ids(&self) -> DisplayRelayIds<'_, Self> {
178 DisplayRelayIds { inner: self }
179 }
180}
181
182impl<T: HasRelayIdsLegacy> HasRelayIds for T {
183 fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
184 match key_type {
185 RelayIdType::Rsa => Some(self.rsa_identity().into()),
186 RelayIdType::Ed25519 => Some(self.ed_identity().into()),
187 }
188 }
189}
190
191#[derive(Clone)]
194pub struct DisplayRelayIds<'a, T: HasRelayIds + ?Sized> {
195 inner: &'a T,
197}
198impl<'a, T: HasRelayIds + ?Sized> fmt::Debug for DisplayRelayIds<'a, T> {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 f.debug_struct("DisplayRelayIds").finish_non_exhaustive()
202 }
203}
204
205impl<'a, T: HasRelayIds + ?Sized> DisplayRelayIds<'a, T> {
206 fn fmt_impl(&self, f: &mut fmt::Formatter<'_>, redact: bool) -> fmt::Result {
208 let mut iter = self.inner.identities();
209 if let Some(ident) = iter.next() {
210 write!(f, "{}", ident.maybe_redacted(redact))?;
211 }
212 if redact {
213 return Ok(());
214 }
215 for ident in iter {
216 write!(f, " {}", ident.maybe_redacted(redact))?;
217 }
218 Ok(())
219 }
220}
221impl<'a, T: HasRelayIds + ?Sized> fmt::Display for DisplayRelayIds<'a, T> {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 self.fmt_impl(f, false)
224 }
225}
226impl<'a, T: HasRelayIds + ?Sized> Redactable for DisplayRelayIds<'a, T> {
227 fn display_redacted(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228 self.fmt_impl(f, true)
229 }
230}
231
232#[derive(Clone)]
234pub struct RelayIdIter<'a, T: HasRelayIds + ?Sized> {
235 info: &'a T,
237 next_key: RelayIdTypeIter,
239}
240
241impl<'a, T: HasRelayIds + ?Sized> Iterator for RelayIdIter<'a, T> {
242 type Item = RelayIdRef<'a>;
243
244 fn next(&mut self) -> Option<Self::Item> {
245 for key_type in &mut self.next_key {
246 if let Some(key) = self.info.identity(key_type) {
247 return Some(key);
248 }
249 }
250 None
251 }
252}
253impl<'a, T: HasRelayIds + ?Sized> FusedIterator for RelayIdIter<'a, T> {}
255
256pub trait HasAddrs {
258 fn addrs(&self) -> impl Iterator<Item = SocketAddr>;
274}
275
276impl<T: HasAddrs> HasAddrs for &T {
277 fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
278 <T as HasAddrs>::addrs(self)
280 }
281}
282
283pub trait HasChanMethod {
285 fn chan_method(&self) -> ChannelMethod;
290}
291
292pub trait DirectChanMethodsHelper: HasAddrs {}
296
297impl<D: DirectChanMethodsHelper> HasChanMethod for D {
298 fn chan_method(&self) -> ChannelMethod {
299 ChannelMethod::Direct(self.addrs().collect_vec())
300 }
301}
302
303pub trait ChanTarget: HasRelayIds + HasAddrs + HasChanMethod {
309 fn display_chan_target(&self) -> DisplayChanTarget<'_, Self>
315 where
316 Self: Sized,
317 {
318 DisplayChanTarget { inner: self }
319 }
320
321 fn all_addrs_allowed_for_outgoing_channels(&self) -> bool {
329 self.addrs().all(|addr| match addr.ip() {
330 IpAddr::V4(v4) => {
331 !(v4.is_loopback() || v4.is_private() || v4.is_unspecified() || v4.is_documentation() || v4.is_multicast() || v4.is_link_local()) }
338 IpAddr::V6(v6) => {
339 !(v6.is_loopback() || v6.is_multicast() || v6.is_unspecified() || v6.is_unique_local() || v6.is_unicast_link_local()) }
345 })
346 }
347
348 fn has_all_nonzero_port(&self) -> bool {
350 self.addrs().all(|addr| addr.port() != 0)
351 }
352}
353
354pub trait CircTarget: ChanTarget {
359 fn linkspecs(&self) -> tor_bytes::EncodeResult<Vec<crate::EncodedLinkSpec>> {
374 let mut result: Vec<_> = self.identities().map(|id| id.to_owned().into()).collect();
375 #[allow(irrefutable_let_patterns)]
376 if let ChannelMethod::Direct(addrs) = self.chan_method() {
377 result.extend(addrs.into_iter().map(crate::LinkSpec::from));
378 }
379 crate::LinkSpec::sort_by_type(&mut result[..]);
380 result.into_iter().map(|ls| ls.encode()).collect()
381 }
382 fn ntor_onion_key(&self) -> &pk::curve25519::PublicKey;
384 fn protovers(&self) -> &tor_protover::Protocols;
386}
387
388#[derive(Debug, Clone)]
391pub struct DisplayChanTarget<'a, T> {
392 inner: &'a T,
394}
395
396impl<'a, T: ChanTarget> DisplayChanTarget<'a, T> {
397 fn fmt_impl(&self, f: &mut fmt::Formatter<'_>, redact: bool) -> fmt::Result {
399 write!(f, "[")?;
400 match self.inner.chan_method() {
404 ChannelMethod::Direct(v) if v.is_empty() => write!(f, "?")?,
405 ChannelMethod::Direct(v) if v.len() == 1 => {
406 write!(f, "{}", v[0].maybe_redacted(redact))?;
407 }
408 ChannelMethod::Direct(v) => write!(f, "{}+", v[0].maybe_redacted(redact))?,
409 #[cfg(feature = "pt-client")]
410 ChannelMethod::Pluggable(target) => {
411 match target.addr() {
412 PtTargetAddr::None => {}
413 other => write!(f, "{} ", other.maybe_redacted(redact))?,
414 }
415 write!(f, "via {}", target.transport())?;
416 }
419 }
420
421 write!(f, " ")?;
422 self.inner.display_relay_ids().fmt_impl(f, redact)?;
423
424 write!(f, "]")
425 }
426}
427
428impl<'a, T: ChanTarget> fmt::Display for DisplayChanTarget<'a, T> {
429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430 self.fmt_impl(f, false)
431 }
432}
433
434impl<'a, T: ChanTarget + fmt::Debug> safelog::Redactable for DisplayChanTarget<'a, T> {
435 fn display_redacted(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436 self.fmt_impl(f, true)
437 }
438 fn debug_redacted(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439 write!(f, "ChanTarget({:?})", self.redacted().to_string())
440 }
441}
442
443#[cfg(test)]
444mod test {
445 #![allow(clippy::bool_assert_comparison)]
447 #![allow(clippy::clone_on_copy)]
448 #![allow(clippy::dbg_macro)]
449 #![allow(clippy::mixed_attributes_style)]
450 #![allow(clippy::print_stderr)]
451 #![allow(clippy::print_stdout)]
452 #![allow(clippy::single_char_pattern)]
453 #![allow(clippy::unwrap_used)]
454 #![allow(clippy::unchecked_time_subtraction)]
455 #![allow(clippy::useless_vec)]
456 #![allow(clippy::needless_pass_by_value)]
457 #![allow(clippy::string_slice)] use super::*;
460 use crate::RelayIds;
461 use hex_literal::hex;
462 use std::net::IpAddr;
463 use tor_llcrypto::pk::{self, ed25519::Ed25519Identity, rsa::RsaIdentity};
464
465 struct Example {
466 addrs: Vec<SocketAddr>,
467 ed_id: pk::ed25519::Ed25519Identity,
468 rsa_id: pk::rsa::RsaIdentity,
469 ntor: pk::curve25519::PublicKey,
470 pv: tor_protover::Protocols,
471 }
472 impl HasAddrs for Example {
473 fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
474 self.addrs.iter().copied()
475 }
476 }
477 impl DirectChanMethodsHelper for Example {}
478 impl HasRelayIdsLegacy for Example {
479 fn ed_identity(&self) -> &pk::ed25519::Ed25519Identity {
480 &self.ed_id
481 }
482 fn rsa_identity(&self) -> &pk::rsa::RsaIdentity {
483 &self.rsa_id
484 }
485 }
486 impl ChanTarget for Example {}
487 impl CircTarget for Example {
488 fn ntor_onion_key(&self) -> &pk::curve25519::PublicKey {
489 &self.ntor
490 }
491 fn protovers(&self) -> &tor_protover::Protocols {
492 &self.pv
493 }
494 }
495
496 fn example() -> Example {
498 Example {
499 addrs: vec![
500 "127.0.0.1:99".parse::<SocketAddr>().unwrap(),
501 "[::1]:909".parse::<SocketAddr>().unwrap(),
502 ],
503 ed_id: pk::ed25519::PublicKey::from_bytes(&hex!(
504 "fc51cd8e6218a1a38da47ed00230f058
505 0816ed13ba3303ac5deb911548908025"
506 ))
507 .unwrap()
508 .into(),
509 rsa_id: pk::rsa::RsaIdentity::from_bytes(&hex!(
510 "1234567890abcdef12341234567890abcdef1234"
511 ))
512 .unwrap(),
513 ntor: pk::curve25519::PublicKey::from(hex!(
514 "e6db6867583030db3594c1a424b15f7c
515 726624ec26b3353b10a903a6d0ab1c4c"
516 )),
517 pv: tor_protover::Protocols::default(),
518 }
519 }
520
521 #[test]
522 fn test_linkspecs() {
523 let ex = example();
524 let specs = ex
525 .linkspecs()
526 .unwrap()
527 .into_iter()
528 .map(|ls| ls.parse())
529 .collect::<Result<Vec<_>, _>>()
530 .unwrap();
531 assert_eq!(4, specs.len());
532
533 use crate::ls::LinkSpec;
534 assert_eq!(
535 specs[0],
536 LinkSpec::OrPort("127.0.0.1".parse::<IpAddr>().unwrap(), 99)
537 );
538 assert_eq!(
539 specs[1],
540 LinkSpec::RsaId(
541 pk::rsa::RsaIdentity::from_bytes(&hex!("1234567890abcdef12341234567890abcdef1234"))
542 .unwrap()
543 )
544 );
545 assert_eq!(
546 specs[2],
547 LinkSpec::Ed25519Id(
548 pk::ed25519::PublicKey::from_bytes(&hex!(
549 "fc51cd8e6218a1a38da47ed00230f058
550 0816ed13ba3303ac5deb911548908025"
551 ))
552 .unwrap()
553 .into()
554 )
555 );
556 assert_eq!(
557 specs[3],
558 LinkSpec::OrPort("::1".parse::<IpAddr>().unwrap(), 909)
559 );
560 }
561
562 #[test]
563 fn cmp_by_ids() {
564 use crate::RelayIds;
565 use std::cmp::Ordering;
566 fn b(ed: Option<Ed25519Identity>, rsa: Option<RsaIdentity>) -> RelayIds {
567 let mut b = RelayIds::builder();
568 if let Some(ed) = ed {
569 b.ed_identity(ed);
570 }
571 if let Some(rsa) = rsa {
572 b.rsa_identity(rsa);
573 }
574 b.build().unwrap()
575 }
576 fn assert_sorted(v: &[RelayIds]) {
578 for slice in v.windows(2) {
579 assert_eq!(slice[0].cmp_by_relay_ids(&slice[1]), Ordering::Less);
580 assert_eq!(slice[1].cmp_by_relay_ids(&slice[0]), Ordering::Greater);
581 assert_eq!(slice[0].cmp_by_relay_ids(&slice[0]), Ordering::Equal);
582 }
583 }
584
585 let ed1 = hex!("0a54686973206973207468652043656e7472616c205363727574696e697a6572").into();
586 let ed2 = hex!("6962696c69747920746f20656e666f72636520616c6c20746865206c6177730a").into();
587 let ed3 = hex!("73736564207965740a497420697320616c736f206d7920726573706f6e736962").into();
588 let rsa1 = hex!("2e2e2e0a4974206973206d7920726573706f6e73").into();
589 let rsa2 = hex!("5468617420686176656e2774206265656e207061").into();
590 let rsa3 = hex!("696c69747920746f20616c65727420656163680a").into();
591
592 assert_sorted(&[
593 b(Some(ed1), None),
594 b(Some(ed2), None),
595 b(Some(ed3), None),
596 b(Some(ed3), Some(rsa1)),
597 ]);
598 assert_sorted(&[
599 b(Some(ed1), Some(rsa3)),
600 b(Some(ed2), Some(rsa2)),
601 b(Some(ed3), Some(rsa1)),
602 b(Some(ed3), Some(rsa2)),
603 ]);
604 assert_sorted(&[
605 b(Some(ed1), Some(rsa1)),
606 b(Some(ed1), Some(rsa2)),
607 b(Some(ed1), Some(rsa3)),
608 ]);
609 assert_sorted(&[
610 b(None, Some(rsa1)),
611 b(None, Some(rsa2)),
612 b(None, Some(rsa3)),
613 ]);
614 assert_sorted(&[
615 b(None, Some(rsa1)),
616 b(Some(ed1), None),
617 b(Some(ed1), Some(rsa1)),
618 ]);
619 }
620
621 #[test]
622 fn compare_id_sets() {
623 let ed1 = hex!("0a54686973206973207468652043656e7472616c205363727574696e697a6572").into();
625 let rsa1 = hex!("2e2e2e0a4974206973206d7920726573706f6e73").into();
626 let rsa2 = RsaIdentity::from(hex!("5468617420686176656e2774206265656e207061"));
627
628 let both1 = RelayIds::builder()
629 .ed_identity(ed1)
630 .rsa_identity(rsa1)
631 .build()
632 .unwrap();
633 let mixed = RelayIds::builder()
634 .ed_identity(ed1)
635 .rsa_identity(rsa2)
636 .build()
637 .unwrap();
638 let ed1 = RelayIds::builder().ed_identity(ed1).build().unwrap();
639 let rsa1 = RelayIds::builder().rsa_identity(rsa1).build().unwrap();
640 let rsa2 = RelayIds::builder().rsa_identity(rsa2).build().unwrap();
641
642 fn chk_equal(v: &impl HasRelayIds) {
643 assert!(v.same_relay_ids(v));
644 assert!(v.has_all_relay_ids_from(v));
645 assert!(v.has_any_relay_id_from(v));
646 }
647 fn chk_strict_subset(bigger: &impl HasRelayIds, smaller: &impl HasRelayIds) {
648 assert!(!bigger.same_relay_ids(smaller));
649 assert!(bigger.has_all_relay_ids_from(smaller));
650 assert!(bigger.has_any_relay_id_from(smaller));
651 assert!(!smaller.same_relay_ids(bigger));
652 assert!(!smaller.has_all_relay_ids_from(bigger));
653 assert!(smaller.has_any_relay_id_from(bigger));
654 }
655 fn chk_nontrivially_overlapping_one_way(a: &impl HasRelayIds, b: &impl HasRelayIds) {
656 assert!(!a.same_relay_ids(b));
657 assert!(!a.has_all_relay_ids_from(b));
658 assert!(a.has_any_relay_id_from(b));
659 }
660 fn chk_nontrivially_overlapping(a: &impl HasRelayIds, b: &impl HasRelayIds) {
661 chk_nontrivially_overlapping_one_way(a, b);
662 chk_nontrivially_overlapping_one_way(b, a);
663 }
664
665 chk_equal(&ed1);
666 chk_equal(&rsa1);
667 chk_equal(&both1);
668
669 chk_strict_subset(&both1, &ed1);
670 chk_strict_subset(&both1, &rsa1);
671 chk_strict_subset(&mixed, &ed1);
672 chk_strict_subset(&mixed, &rsa2);
673
674 chk_nontrivially_overlapping(&both1, &mixed);
675 }
676
677 #[test]
678 fn display() {
679 let e1 = example();
680 assert_eq!(
681 e1.display_chan_target().to_string(),
682 "[127.0.0.1:99+ ed25519:/FHNjmIYoaONpH7QAjDwWAgW7RO6MwOsXeuRFUiQgCU \
683 $1234567890abcdef12341234567890abcdef1234]"
684 );
685
686 #[cfg(feature = "pt-client")]
687 {
688 use crate::PtTarget;
689
690 let rsa = hex!("234461644a6f6b6523436f726e794f6e4d61696e").into();
691 let mut b = crate::OwnedChanTarget::builder();
692 b.ids().rsa_identity(rsa);
693 let e2 = b
694 .method(ChannelMethod::Pluggable(PtTarget::new(
695 "obfs4".parse().unwrap(),
696 "127.0.0.1:99".parse().unwrap(),
697 )))
698 .build()
699 .unwrap();
700 assert_eq!(
701 e2.to_string(),
702 "[127.0.0.1:99 via obfs4 $234461644a6f6b6523436f726e794f6e4d61696e]"
703 );
704 }
705 }
706
707 #[test]
708 fn has_id() {
709 use crate::RelayIds;
710 assert!(example().has_any_identity());
711 assert!(!RelayIds::empty().has_any_identity());
712 }
713}