//! Implement RPC functionality for finding what ports are running as proxies. use std::{net::SocketAddr, sync::Arc}; use tor_error::{ErrorKind, HasKind}; use tor_rpcbase::{self as rpc}; use crate::proxy::port_info; use super::session::ArtiRpcSession; /// Representation of a single proxy, as delivered by the RPC API. #[derive(serde::Serialize, Clone, Debug)] #[cfg_attr(test, derive(PartialEq, Eq))] pub(super) struct Proxy { /// Where the proxy is listening, what protocol it speaks, /// and what protocol-specific options it expects. pub(super) listener: ProxyListener, } /// Representation of a single proxy's listener location, as delivered by the RPC API. #[derive(serde::Serialize, Clone, Debug)] #[cfg_attr(test, derive(PartialEq, Eq))] pub(super) enum ProxyListener { /// A SOCKS5 proxy. #[serde(rename = "socks5")] Socks5 { /// The address at which we're listening for SOCKS connections. tcp_address: Option, }, /// An HTTP CONNECT proxy. #[cfg(feature = "http-connect")] #[serde(rename = "http_connect")] HttpConnect { /// The address at which we're listening for HTTP CONNECT connections. tcp_address: Option, }, } impl ProxyListener { /// Try to represent the given port as a ProxyListener. /// /// Return None if it cannot be represented. pub(crate) fn try_from_portinfo(port: &port_info::Port) -> Option { use port_info::SupportedProtocol as SP; use tor_rtcompat::general::{self, SocketAddr::Inet}; match (&port.address, &port.protocol) { (Inet(a), SP::Socks) => Some(Self::Socks5 { tcp_address: Some(*a), }), #[cfg(feature = "http-connect")] (Inet(a), SP::Http) => Some(Self::HttpConnect { tcp_address: Some(*a), }), (Inet(_), SP::DnsUdp) => None, // TODO: Handle unix addresses once we can bind to them (general::SocketAddr::Unix(_), _) => None, (_, _) => None, } } } /// A representation of the set of proxy addresses available from the RPC API. #[derive(serde::Serialize, Clone, Debug)] #[cfg_attr(test, derive(PartialEq, Eq))] pub(super) struct ProxyInfo { /// A list of the supported proxies. pub(super) proxies: Vec, } /// Get a list of all the currently running proxies. /// /// This method should not be used when deciding which proxy /// an RPC application should connect to. /// Instead, the application should use /// [`arti:get_rpc_proxy_info`](GetRpcProxyInfo). #[derive(Debug, serde::Deserialize, derive_deftly::Deftly)] #[derive_deftly(rpc::DynMethod)] #[deftly(rpc(method_name = "arti:get_proxy_info"))] struct GetProxyInfo {} /// Get a list of the currently running proxies /// that are integrated with the RPC system. /// /// This method returns a list of proxies. /// The RPC application may be not be able to use all proxies from the list, /// and may prefer some proxies over other. /// When multiple proxies are equally preferred, /// the application SHOULD use whichever appears first in the list. /// /// You typically won't need to invoke this method yourself: /// your RPC library (like `arti-rpc-client-core`) /// should take care if it for you. #[derive(Debug, serde::Deserialize, derive_deftly::Deftly)] #[derive_deftly(rpc::DynMethod)] #[deftly(rpc(method_name = "arti:get_rpc_proxy_info"))] struct GetRpcProxyInfo {} impl rpc::RpcMethod for GetProxyInfo { type Output = ProxyInfo; type Update = rpc::NoUpdates; } impl rpc::RpcMethod for GetRpcProxyInfo { type Output = ProxyInfo; type Update = rpc::NoUpdates; } /// An error encountered while asking for the proxy addresses. #[derive(Clone, Debug, thiserror::Error)] enum GetProxyInfoError { /// The Sender was dropped without setting any proxy info; /// likely, Arti is shutting down. #[error("Arti appears to be shutting down")] Shutdown, } impl HasKind for GetProxyInfoError { fn kind(&self) -> ErrorKind { use GetProxyInfoError as E; match self { E::Shutdown => ErrorKind::ArtiShuttingDown, } } } /// Implementation for GetProxyInfo on ArtiRpcSession. async fn rpc_session_get_proxy_info( session: Arc, _method: Box, _ctx: Arc, ) -> Result { let proxy_info = session.arti_state.get_proxy_info().await; match proxy_info { Ok(info) => Ok((*info).clone()), Err(()) => Err(GetProxyInfoError::Shutdown), } } rpc::static_rpc_invoke_fn! {rpc_session_get_proxy_info;} /// Implementation for GetProxyInfo on ArtiRpcSession. async fn rpc_session_get_rpc_proxy_info( session: Arc, _method: Box, _ctx: Arc, ) -> Result { let proxy_info = session.arti_state.get_proxy_info().await; match proxy_info { Ok(info) => Ok((*info).clone()), Err(()) => Err(GetProxyInfoError::Shutdown), } } rpc::static_rpc_invoke_fn! {rpc_session_get_rpc_proxy_info;}