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

            
3
use std::{net::TcpStream, sync::Arc};
4

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

            
8
use fs_mistrust::Mistrust;
9
use tor_general_addr::general;
10

            
11
use 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]
19
pub 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]
31
pub 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

            
40
impl 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
}
53
impl 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
}
62
impl 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
}