1
//! Implement RPC functionality for finding what ports are running as proxies.
2

            
3
use std::{net::SocketAddr, sync::Arc};
4
use tor_error::{ErrorKind, HasKind};
5
use tor_rpcbase::{self as rpc};
6

            
7
use crate::proxy::port_info;
8

            
9
use super::session::ArtiRpcSession;
10

            
11
/// Representation of a single proxy, as delivered by the RPC API.
12
#[derive(serde::Serialize, Clone, Debug)]
13
#[cfg_attr(test, derive(PartialEq, Eq))]
14
pub(super) struct Proxy {
15
    /// Where the proxy is listening, what protocol it speaks,
16
    /// and what protocol-specific options it expects.
17
    pub(super) listener: ProxyListener,
18
}
19

            
20
/// Representation of a single proxy's listener location, as delivered by the RPC API.
21
#[derive(serde::Serialize, Clone, Debug)]
22
#[cfg_attr(test, derive(PartialEq, Eq))]
23
pub(super) enum ProxyListener {
24
    /// A SOCKS5 proxy.
25
    #[serde(rename = "socks5")]
26
    Socks5 {
27
        /// The address at which we're listening for SOCKS connections.
28
        tcp_address: Option<SocketAddr>,
29
    },
30
    /// An HTTP CONNECT proxy.
31
    #[cfg(feature = "http-connect")]
32
    #[serde(rename = "http_connect")]
33
    HttpConnect {
34
        /// The address at which we're listening for HTTP CONNECT connections.
35
        tcp_address: Option<SocketAddr>,
36
    },
37
}
38

            
39
impl ProxyListener {
40
    /// Try to represent the given port as a ProxyListener.
41
    ///
42
    /// Return None if it cannot be represented.
43
4
    pub(crate) fn try_from_portinfo(port: &port_info::Port) -> Option<Self> {
44
        use port_info::SupportedProtocol as SP;
45
        use tor_rtcompat::general::{self, SocketAddr::Inet};
46

            
47
4
        match (&port.address, &port.protocol) {
48
4
            (Inet(a), SP::Socks) => Some(Self::Socks5 {
49
4
                tcp_address: Some(*a),
50
4
            }),
51
            #[cfg(feature = "http-connect")]
52
            (Inet(a), SP::Http) => Some(Self::HttpConnect {
53
                tcp_address: Some(*a),
54
            }),
55
            (Inet(_), SP::DnsUdp) => None,
56
            // TODO: Handle unix addresses once we can bind to them
57
            (general::SocketAddr::Unix(_), _) => None,
58
            (_, _) => None,
59
        }
60
4
    }
61
}
62

            
63
/// A representation of the set of proxy addresses available from the RPC API.
64
#[derive(serde::Serialize, Clone, Debug)]
65
#[cfg_attr(test, derive(PartialEq, Eq))]
66
pub(super) struct ProxyInfo {
67
    /// A list of the supported proxies.
68
    pub(super) proxies: Vec<Proxy>,
69
}
70

            
71
/// Get a list of all the currently running proxies.
72
///
73
/// This method should not be used when deciding which proxy
74
/// an RPC application should connect to.
75
/// Instead, the application should use
76
/// [`arti:get_rpc_proxy_info`](GetRpcProxyInfo).
77
#[derive(Debug, serde::Deserialize, derive_deftly::Deftly)]
78
#[derive_deftly(rpc::DynMethod)]
79
#[deftly(rpc(method_name = "arti:get_proxy_info"))]
80
struct GetProxyInfo {}
81

            
82
/// Get a list of the currently running proxies
83
/// that are integrated with the RPC system.
84
///
85
/// This method returns a list of proxies.
86
/// The RPC application may be not be able to use all proxies from the list,
87
/// and may prefer some proxies over other.
88
/// When multiple proxies are equally preferred,
89
/// the application SHOULD use whichever appears first in the list.
90
///
91
/// You typically won't need to invoke this method yourself:
92
/// your RPC library (like `arti-rpc-client-core`)
93
/// should take care if it for you.
94
#[derive(Debug, serde::Deserialize, derive_deftly::Deftly)]
95
#[derive_deftly(rpc::DynMethod)]
96
#[deftly(rpc(method_name = "arti:get_rpc_proxy_info"))]
97
struct GetRpcProxyInfo {}
98

            
99
impl rpc::RpcMethod for GetProxyInfo {
100
    type Output = ProxyInfo;
101
    type Update = rpc::NoUpdates;
102
}
103

            
104
impl rpc::RpcMethod for GetRpcProxyInfo {
105
    type Output = ProxyInfo;
106
    type Update = rpc::NoUpdates;
107
}
108

            
109
/// An error encountered while asking for the proxy addresses.
110
#[derive(Clone, Debug, thiserror::Error)]
111
enum GetProxyInfoError {
112
    /// The Sender was dropped without setting any proxy info;
113
    /// likely, Arti is shutting down.
114
    #[error("Arti appears to be shutting down")]
115
    Shutdown,
116
}
117
impl HasKind for GetProxyInfoError {
118
    fn kind(&self) -> ErrorKind {
119
        use GetProxyInfoError as E;
120
        match self {
121
            E::Shutdown => ErrorKind::ArtiShuttingDown,
122
        }
123
    }
124
}
125

            
126
/// Implementation for GetProxyInfo on ArtiRpcSession.
127
async fn rpc_session_get_proxy_info(
128
    session: Arc<ArtiRpcSession>,
129
    _method: Box<GetProxyInfo>,
130
    _ctx: Arc<dyn rpc::Context>,
131
) -> Result<ProxyInfo, GetProxyInfoError> {
132
    let proxy_info = session.arti_state.get_proxy_info().await;
133

            
134
    match proxy_info {
135
        Ok(info) => Ok((*info).clone()),
136
        Err(()) => Err(GetProxyInfoError::Shutdown),
137
    }
138
}
139
rpc::static_rpc_invoke_fn! {rpc_session_get_proxy_info;}
140

            
141
/// Implementation for GetProxyInfo on ArtiRpcSession.
142
async fn rpc_session_get_rpc_proxy_info(
143
    session: Arc<ArtiRpcSession>,
144
    _method: Box<GetRpcProxyInfo>,
145
    _ctx: Arc<dyn rpc::Context>,
146
) -> Result<ProxyInfo, GetProxyInfoError> {
147
    let proxy_info = session.arti_state.get_proxy_info().await;
148

            
149
    match proxy_info {
150
        Ok(info) => Ok((*info).clone()),
151
        Err(()) => Err(GetProxyInfoError::Shutdown),
152
    }
153
}
154
rpc::static_rpc_invoke_fn! {rpc_session_get_rpc_proxy_info;}