tor_proto/peer.rs
1//! Code for anything related to a peer as in the endpoint of a tor channel.
2//!
3//! Peer information is put into a [`crate::channel::Channel`] which contains the information that
4//! has been used to connect to it.
5
6use std::net::{IpAddr, SocketAddr};
7
8use tor_linkspec::{BridgeAddr, ChannelMethod, HasRelayIds, RelayIdRef, RelayIdType, RelayIds};
9
10#[cfg(feature = "pt-client")]
11use tor_linkspec::{PtTarget, PtTargetAddr};
12
13/// Represents the address of a connected peer used for a tor channel.
14///
15/// Clever observer here would see that this is basically a [`tor_linkspec::ChannelMethod`] which
16/// has a Direct variant with a vector of address which is incoherent with the semantic of "where we
17/// are connected to".
18#[derive(Clone, Debug, derive_more::Display, Eq, PartialEq)]
19#[non_exhaustive]
20pub enum PeerAddr {
21 /// The socket address we are directly connected to.
22 Direct(SocketAddr),
23 /// The pluggable transport target used to connect.
24 ///
25 /// Note that this is not a specific address but the PT transport details. We keep those
26 /// because if the [`PtTargetAddr`] happens to be None, we need to compare the transport name
27 /// and settings to match a channel.
28 #[cfg(feature = "pt-client")]
29 Pt(PtTarget),
30}
31
32impl PeerAddr {
33 /// Unspecified address used for placeholder in unit tests.
34 #[cfg(any(test, feature = "testing"))]
35 pub(crate) const UNSPECIFIED: Self = Self::Direct(SocketAddr::new(
36 IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED),
37 0,
38 ));
39
40 /// Return the IP address that should be used in the NETINFO cell.
41 ///
42 /// A None value implies we didn't use an IP address to connect (see [`PtTarget`]). The
43 /// [`tor_cell::chancell::msg::Netinfo`] interface will treat a None as an IPv4 unspecified
44 /// address (0.0.0.0).
45 pub fn netinfo_addr(&self) -> Option<IpAddr> {
46 self.socket_addr().map(|sa| sa.ip())
47 }
48
49 /// Return the socket address of this peer.
50 ///
51 /// None means that this is a PT and doesn't use an internet address. One example is it uses a
52 /// hostname.
53 pub fn socket_addr(&self) -> Option<SocketAddr> {
54 match self {
55 PeerAddr::Direct(sa) => Some(*sa),
56 #[cfg(feature = "pt-client")]
57 PeerAddr::Pt(t) => match t.addr() {
58 PtTargetAddr::IpPort(sa) => Some(*sa),
59 _ => None,
60 },
61 }
62 }
63}
64
65impl From<SocketAddr> for PeerAddr {
66 fn from(sa: SocketAddr) -> Self {
67 Self::Direct(sa)
68 }
69}
70
71#[cfg(feature = "pt-client")]
72impl From<PtTarget> for PeerAddr {
73 fn from(t: PtTarget) -> Self {
74 Self::Pt(t)
75 }
76}
77
78/// Useful because [`BridgeAddr`] are used in some Error struct.
79impl From<&PeerAddr> for Option<BridgeAddr> {
80 fn from(t: &PeerAddr) -> Self {
81 match t {
82 PeerAddr::Direct(sa) => Some(BridgeAddr::new_addr_from_sockaddr(*sa)),
83 #[cfg(feature = "pt-client")]
84 PeerAddr::Pt(target) => target.addr().clone().into(),
85 }
86 }
87}
88
89/// Represents the actual information about the peer of a [`crate::channel::Channel`].
90///
91/// This type exists because [`tor_linkspec::OwnedChanTarget`] is overloaded and used for multiple
92/// purposes.
93///
94/// An [`tor_linkspec::OwnedChanTarget`] represents an intended target, rather than what is
95/// actually used. In addition, it stores peer addresses both in a vector inside the struct and
96/// within the [`ChannelMethod`]. When connecting to the peer, our code returns an
97/// [`tor_linkspec::OwnedChanTarget`] whose [`ChannelMethod`] has been filtered to contain only the
98/// address that was actually used. However, the [`tor_linkspec::HasAddrs`] trait reads from the
99/// address vector, which creates a disconnect between the intent and the actual usage.
100///
101/// This struct resolves that ambiguity by storing the concrete peer information that was actually
102/// used. It provides clear, unambiguous guarantees about the peer associated with the channel.
103#[derive(Clone, Debug)]
104pub struct PeerInfo {
105 /// Actual target address used for the channel connection.
106 addr: PeerAddr,
107 /// Identities that this relay provides.
108 ids: RelayIds,
109}
110
111impl PeerInfo {
112 /// Empty peer info used for placeholder in unit tests.
113 #[cfg(any(test, feature = "testing"))]
114 pub(crate) const EMPTY: Self = Self {
115 addr: PeerAddr::UNSPECIFIED,
116 ids: RelayIds::empty(),
117 };
118
119 /// Constructor.
120 pub(crate) fn new(addr: PeerAddr, ids: RelayIds) -> Self {
121 Self { addr, ids }
122 }
123
124 /// Return a reference to the target address.
125 pub(crate) fn addr(&self) -> &PeerAddr {
126 &self.addr
127 }
128
129 /// Return a reference to the [`RelayIds`] of this channel target.
130 pub(crate) fn ids(&self) -> &RelayIds {
131 &self.ids
132 }
133
134 /// Return true iff the given [`ChannelMethod`] matches us.
135 ///
136 /// A [`ChannelMethod`] is semantically for the concept of a "target" as in a connection goal.
137 /// And so, this method returns true or false if the target method matches the peer
138 /// information. This is used to choose the best channel in the channel manager.
139 ///
140 /// A match is true if:
141 /// * Direct: Our address is contained in the method set of addresses.
142 /// * Pluggable: Our address is equal to the method's target.
143 pub fn matches_chan_method(&self, method: &ChannelMethod) -> bool {
144 match (method, self.addr()) {
145 (ChannelMethod::Direct(addrs), PeerAddr::Direct(our_addr)) => addrs.contains(our_addr),
146 #[cfg(feature = "pt-client")]
147 (ChannelMethod::Pluggable(target), PeerAddr::Pt(our_target)) => our_target == target,
148 _ => false,
149 }
150 }
151}
152
153impl std::fmt::Display for PeerInfo {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 write!(f, "{} [{}]", self.addr, self.ids)
156 }
157}
158
159impl HasRelayIds for PeerInfo {
160 fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
161 self.ids().identity(key_type)
162 }
163}