1
//! Client operations for working with connect points.
2

            
3
use std::{io, net::TcpStream};
4

            
5
#[cfg(unix)]
6
use std::os::unix::net::UnixStream;
7

            
8
use fs_mistrust::Mistrust;
9

            
10
use 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]
17
pub 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

            
27
impl 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
}
40
impl 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
}
49
impl 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
}