Skip to main content

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}