Skip to main content

tor_rpc_connect/
client.rs

1//! Client operations for working with connect points.
2
3use std::{net::TcpStream, sync::Arc};
4
5#[cfg(unix)]
6use std::os::unix::net::UnixStream;
7
8use fs_mistrust::Mistrust;
9use tor_general_addr::general;
10
11use crate::{
12    ConnectError, ResolvedConnectPoint,
13    auth::{RpcAuth, RpcCookieSource, cookie::CookieLocation},
14    connpt::{AddrWithStr, AddressFile},
15};
16
17/// Information about an initial connection to a connect point.
18#[non_exhaustive]
19pub struct Connection {
20    /// A successfully connected stream.
21    ///
22    /// At the time this is returned, authentication has not yet completed.
23    pub stream: Stream,
24
25    /// Information about how to authenticate.
26    pub auth: crate::auth::RpcAuth,
27}
28
29/// A connection to Arti that can be used to transfer data.
30#[non_exhaustive]
31pub enum Stream {
32    /// A connection via TCP.
33    Tcp(TcpStream),
34
35    /// A connection via an AF_UNIX stream socket.
36    #[cfg(unix)]
37    Unix(UnixStream),
38}
39
40impl ResolvedConnectPoint {
41    /// Open a new connection to the RPC server designated by this connect point.
42    ///
43    /// On success, return a Connection structure containing a newly open socket,
44    /// and instructions about how to authenticate on that socket.
45    pub fn connect(&self, mistrust: &Mistrust) -> Result<Connection, ConnectError> {
46        use crate::connpt::ConnectPointEnum as CptE;
47        match &self.0 {
48            CptE::Connect(connect) => connect.do_connect(mistrust),
49            CptE::Builtin(builtin) => builtin.do_connect(),
50        }
51    }
52}
53impl crate::connpt::Builtin {
54    /// Try to connect on a "builtin" connect point.
55    fn do_connect(&self) -> Result<Connection, ConnectError> {
56        use crate::connpt::BuiltinVariant as BV;
57        match self.builtin {
58            BV::Abort => Err(ConnectError::ExplicitAbort),
59        }
60    }
61}
62impl crate::connpt::Connect<crate::connpt::Resolved> {
63    /// Return the address that we should actually try to connect to, with its string representation
64    /// set to the canonical address.
65    fn find_connect_address(
66        &self,
67        mistrust: &Mistrust,
68    ) -> Result<AddrWithStr<general::SocketAddr>, ConnectError> {
69        use crate::connpt::ConnectAddress::*;
70
71        // Find the target address.
72        let mut addr = match &self.socket {
73            InetAuto(auto_addr) => {
74                let socket_address_file = self.socket_address_file.as_ref().ok_or_else(|| {
75                    ConnectError::Internal(
76                        "Absent socket_address_file should have been rejected earlier".into(),
77                    )
78                })?;
79                let addr_from_disk = mistrust
80                    .verifier()
81                    .permit_readable()
82                    .file_access()
83                    .read_to_string(socket_address_file)
84                    .map_err(ConnectError::SocketAddressFileAccess)?;
85                let addrfile: AddressFile = serde_json::from_str(&addr_from_disk)
86                    .map_err(|e| ConnectError::SocketAddressFileJson(Arc::new(e)))?;
87                let address: AddrWithStr<general::SocketAddr> = addrfile
88                    .address
89                    .parse()
90                    .map_err(ConnectError::SocketAddressFileContent)?;
91                auto_addr.validate_parsed_address(address.as_ref())?;
92                address
93            }
94            Socket(addr) => addr.clone(),
95        };
96        // Override the string if needed.
97        if let Some(canon) = &self.socket_canonical {
98            addr.set_string_from(canon);
99        }
100        Ok(addr)
101    }
102
103    /// Try to connect on a "Connect" connect point.
104    fn do_connect(&self, mistrust: &Mistrust) -> Result<Connection, ConnectError> {
105        use crate::connpt::Auth;
106        use tor_general_addr::general::SocketAddr as SA;
107        let connect_to_address = self.find_connect_address(mistrust)?;
108        let auth = match &self.auth {
109            Auth::None => RpcAuth::Inherent,
110            Auth::Cookie { path } => RpcAuth::Cookie {
111                secret: RpcCookieSource::Unloaded(CookieLocation {
112                    path: path.clone(),
113                    mistrust: mistrust.clone(),
114                }),
115                server_address: connect_to_address.as_str().to_string(),
116            },
117            // This is unreachable, but harmless:
118            Auth::Unrecognized(_) => return Err(ConnectError::UnsupportedAuthType),
119        };
120        if let Some(sock_parent_dir) = crate::socket_parent_path(connect_to_address.as_ref()) {
121            mistrust.check_directory(sock_parent_dir)?;
122        }
123        let stream = match connect_to_address.as_ref() {
124            SA::Inet(addr) => {
125                let socket = TcpStream::connect(addr)?;
126                Stream::Tcp(socket)
127            }
128            #[cfg(unix)]
129            SA::Unix(addr) => {
130                let socket = UnixStream::connect_addr(addr)?;
131                Stream::Unix(socket)
132            }
133            _ => return Err(ConnectError::UnsupportedSocketType),
134        };
135
136        Ok(Connection { stream, auth })
137    }
138}