tor_rpc_connect/
client.rs

1//! Client operations for working with connect points.
2
3use std::{io, net::TcpStream};
4
5#[cfg(unix)]
6use std::os::unix::net::UnixStream;
7
8use fs_mistrust::Mistrust;
9
10use crate::{
11    auth::{cookie::CookieLocation, RpcAuth, RpcCookieSource},
12    ConnectError, ResolvedConnectPoint,
13};
14
15/// Information about an initial connection to a connect point.
16#[non_exhaustive]
17pub struct Connection {
18    /// A reading instance successfully connected socket.
19    pub reader: Box<dyn io::Read + Send>,
20    /// A writing instance successfully connected socket.
21    pub writer: Box<dyn io::Write + Send>,
22
23    /// Information about how to authenticate.
24    pub auth: crate::auth::RpcAuth,
25}
26
27impl ResolvedConnectPoint {
28    /// Open a new connection to the RPC server designated by this connect point.
29    ///
30    /// On success, return a Connection structure containing a newly open socket,
31    /// and instructions about how to authenticate on that socket.
32    pub fn connect(&self, mistrust: &Mistrust) -> Result<Connection, ConnectError> {
33        use crate::connpt::ConnectPointEnum as CptE;
34        match &self.0 {
35            CptE::Connect(connect) => connect.do_connect(mistrust),
36            CptE::Builtin(builtin) => builtin.do_connect(),
37        }
38    }
39}
40impl crate::connpt::Builtin {
41    /// Try to connect on a "builtin" connect point.
42    fn do_connect(&self) -> Result<Connection, ConnectError> {
43        use crate::connpt::BuiltinVariant as BV;
44        match self.builtin {
45            BV::Abort => Err(ConnectError::ExplicitAbort),
46        }
47    }
48}
49impl crate::connpt::Connect<crate::connpt::Resolved> {
50    /// Try to connect on a "Connect" connect point.
51    fn do_connect(&self, mistrust: &Mistrust) -> Result<Connection, ConnectError> {
52        use crate::connpt::Auth;
53        use tor_general_addr::general::SocketAddr as SA;
54        let auth = match &self.auth {
55            Auth::None => RpcAuth::Inherent,
56            Auth::Cookie { path } => {
57                let canonical_addr = self.socket_canonical.as_ref().unwrap_or(&self.socket);
58                RpcAuth::Cookie {
59                    secret: RpcCookieSource::Unloaded(CookieLocation {
60                        path: path.clone(),
61                        mistrust: mistrust.clone(),
62                    }),
63                    server_address: canonical_addr.as_str().to_string(),
64                }
65            }
66            // This is unreachable, but harmless:
67            Auth::Unrecognized(_) => return Err(ConnectError::UnsupportedAuthType),
68        };
69        if let Some(sock_parent_dir) = crate::socket_parent_path(self.socket.as_ref()) {
70            mistrust.check_directory(sock_parent_dir)?;
71        }
72        // TODO: we currently use try_clone() to get separate reader and writer instances.
73        // conceivably, we could instead create something like the `Split` implementation that
74        // exists for `AsyncRead + AsyncWrite` objects in futures::io.
75        let (reader, writer): (Box<dyn io::Read + Send>, Box<dyn io::Write + Send>) =
76            match self.socket.as_ref() {
77                SA::Inet(addr) => {
78                    let socket = TcpStream::connect(addr)?;
79                    (Box::new(socket.try_clone()?), Box::new(socket))
80                }
81                #[cfg(unix)]
82                SA::Unix(addr) => {
83                    let socket = UnixStream::connect_addr(addr)?;
84                    (Box::new(socket.try_clone()?), Box::new(socket))
85                }
86                _ => return Err(ConnectError::UnsupportedSocketType),
87            };
88
89        Ok(Connection {
90            reader,
91            writer,
92            auth,
93        })
94    }
95}