1
//! Declare the TargetPort type.
2

            
3
use serde::{Deserialize, Serialize};
4
use std::fmt;
5

            
6
/// A port that we want to connect to as a client.
7
///
8
/// Ordinarily, this is a TCP port, plus a flag to indicate whether we
9
/// must support IPv4 or IPv6.
10
#[derive(
11
    Clone, Copy, Debug, Deserialize, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Default,
12
)]
13
#[non_exhaustive]
14
pub struct TargetPort {
15
    /// True if this is a request to connect to an IPv6 address
16
    pub ipv6: bool,
17
    /// The port that the client wants to connect to
18
    pub port: u16,
19
}
20

            
21
impl TargetPort {
22
    /// Create a request to make sure that a circuit supports a given
23
    /// ipv4 exit port.
24
5846
    pub fn ipv4(port: u16) -> TargetPort {
25
5846
        TargetPort { ipv6: false, port }
26
5846
    }
27

            
28
    /// Create a request to make sure that a circuit supports a given
29
    /// ipv6 exit port.
30
326
    pub fn ipv6(port: u16) -> TargetPort {
31
326
        TargetPort { ipv6: true, port }
32
326
    }
33

            
34
    /// Return true if this port is supported by the provided Relay.
35
8767144
    pub fn is_supported_by(&self, r: &tor_netdir::details::RelayDetails<'_>) -> bool {
36
8767144
        if self.ipv6 {
37
140
            r.supports_exit_port_ipv6(self.port)
38
        } else {
39
8767004
            r.supports_exit_port_ipv4(self.port)
40
        }
41
8767144
    }
42
}
43

            
44
impl fmt::Display for TargetPort {
45
108
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46
108
        write!(f, "{}{}", self.port, if self.ipv6 { "v6" } else { "v4" })
47
108
    }
48
}