//! Client operations for working with connect points. use std::{fmt, io, net::TcpStream}; #[cfg(unix)] use std::os::unix::net::UnixStream; use fs_mistrust::Mistrust; use crate::{ auth::{Cookie, RpcAuth}, ConnectError, ResolvedConnectPoint, }; /// Wrapper type for a socket that might be a `UnixStream` or a TcpSocket. pub trait AnySocket: io::Read + io::Write + fmt::Debug {} impl AnySocket for T where T: io::Read + io::Write + fmt::Debug {} /// Information about an initial connection to a connect point. #[derive(Debug)] #[non_exhaustive] pub struct Connection { /// A successfully connected socket. pub socket: Box, /// Information about how to authenticate. pub auth: crate::auth::RpcAuth, } impl ResolvedConnectPoint { /// Open a new connection to the RPC server designated by this connect point. /// /// On success, return a Connection structure containing a newly open socket, /// and instructions about how to authenticate on that socket. pub fn connect(&self, mistrust: &Mistrust) -> Result { use crate::connpt::ConnectPointEnum as CptE; match &self.0 { CptE::Connect(connect) => connect.do_connect(mistrust), CptE::Builtin(builtin) => builtin.do_connect(), } } } impl crate::connpt::Builtin { /// Try to connect on a "builtin" connect point. fn do_connect(&self) -> Result { use crate::connpt::BuiltinVariant as BV; match self.builtin { BV::Abort => Err(ConnectError::ExplicitAbort), } } } impl crate::connpt::Connect { /// Try to connect on a "Connect" connect point. fn do_connect(&self, mistrust: &Mistrust) -> Result { use crate::connpt::Auth; use tor_general_addr::general::SocketAddr as SA; let auth = match &self.auth { Auth::None => RpcAuth::None, Auth::Cookie { path } => { let canonical_addr = self.socket_canonical.as_ref().unwrap_or(&self.socket); RpcAuth::Cookie { // TODO RPC: We may want to _defer_ this load operations until the RPC connect // has been accepted, to ensure that we can't read a partial cookie. secret: Cookie::load(path.as_path(), mistrust)?, server_address: canonical_addr.as_str().to_string(), } } // This is unreachable, but harmless: Auth::Unrecognized {} => return Err(ConnectError::UnsupportedAuthType), }; if let Some(sock_parent_dir) = crate::socket_parent_path(self.socket.as_ref())? { mistrust.check_directory(sock_parent_dir)?; } let socket: Box = match self.socket.as_ref() { SA::Inet(addr) => Box::new(TcpStream::connect(addr)?), #[cfg(unix)] SA::Unix(addr) => Box::new(UnixStream::connect_addr(addr)?), _ => return Err(ConnectError::UnsupportedSocketType), }; Ok(Connection { socket, auth }) } }