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

            
6
use std::net::{IpAddr, SocketAddr};
7

            
8
use tor_linkspec::{BridgeAddr, ChannelMethod, HasRelayIds, RelayIdRef, RelayIdType, RelayIds};
9

            
10
#[cfg(feature = "pt-client")]
11
use 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]
20
pub 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

            
32
impl 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
98
    pub fn netinfo_addr(&self) -> Option<IpAddr> {
46
101
        self.socket_addr().map(|sa| sa.ip())
47
98
    }
48

            
49
    /// Return the socket address of this peer.
50
    ///
51
    /// None means that this is a PT and doesn't use an internet addres. One example is it uses a
52
    /// hostname.
53
148
    pub fn socket_addr(&self) -> Option<SocketAddr> {
54
148
        match self {
55
148
            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
148
    }
63
}
64

            
65
impl From<SocketAddr> for PeerAddr {
66
48
    fn from(sa: SocketAddr) -> Self {
67
48
        Self::Direct(sa)
68
48
    }
69
}
70

            
71
#[cfg(feature = "pt-client")]
72
impl 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.
79
impl 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)]
104
pub 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

            
111
impl 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
48
    pub(crate) fn new(addr: PeerAddr, ids: RelayIds) -> Self {
121
48
        Self { addr, ids }
122
48
    }
123

            
124
    /// Return a reference to the target address.
125
100
    pub(crate) fn addr(&self) -> &PeerAddr {
126
100
        &self.addr
127
100
    }
128

            
129
    /// Return a reference to the [`RelayIds`] of this channel target.
130
50
    pub(crate) fn ids(&self) -> &RelayIds {
131
50
        &self.ids
132
50
    }
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

            
153
impl std::fmt::Display for PeerInfo {
154
2
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155
2
        write!(f, "{} [{}]", self.addr, self.ids)
156
2
    }
157
}
158

            
159
impl HasRelayIds for PeerInfo {
160
    fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
161
        self.ids().identity(key_type)
162
    }
163
}