arti_rpcserver/
session.rs

1//! High-level APIs for an RPC session
2//!
3//! A "session" is created when a user authenticates on an RPC connection.  It
4//! is the root for all other RPC capabilities.
5
6use arti_client::{
7    rpc::{ClientConnectionResult, ConnectWithPrefs, ResolvePtrWithPrefs, ResolveWithPrefs},
8    TorClient,
9};
10use derive_deftly::Deftly;
11use std::{net::IpAddr, sync::Arc};
12use tor_error::into_internal;
13use tor_rtcompat::Runtime;
14
15use tor_rpcbase::{self as rpc, static_rpc_invoke_fn, templates::*};
16
17/// An authenticated RPC session: a capability through which most other RPC functionality is available
18///
19/// This relates to [`Connection`](crate::Connection) as follows:
20///
21///  * A `Connection` exists prior to authentication;
22///    whereas an `RpcSession` comes into being as a result of authentication.
23///
24///  * The `RpcSession` is principally owned by the `Connection`'s object table.
25///
26///  * Typically, after authentication, there is one `RpcSession` for the `Connection`.
27///    But a client may authenticate more than once; each time produces a new `RpcSession`.
28///
29/// ## In the arti rpc system
30///
31/// Base type for an authenticated RPC session.
32///
33/// Upon successful authentication via `auth:authenticate`,
34/// a connection will return either a Session object of this type,
35/// or a Session object that wraps this type.
36/// All other useful objects are available via an RPC session.
37///
38/// This ObjectID for this object can be used as the target of a SOCKS stream.
39#[derive(Deftly)]
40#[derive_deftly(Object)]
41#[deftly(rpc(expose_outside_of_session))]
42pub struct RpcSession {
43    /// An inner TorClient object that we use to implement remaining
44    /// functionality.
45    #[allow(unused)]
46    client: Arc<dyn Client>,
47}
48
49/// Type-erased `TorClient`, as used within an RpcSession.
50trait Client: rpc::Object {
51    /// Return a new isolated TorClient.
52    fn isolated_client(&self) -> Arc<dyn rpc::Object>;
53
54    /// Upcast `self` to an rpc::Object.
55    fn upcast_arc(self: Arc<Self>) -> Arc<dyn rpc::Object>;
56}
57
58impl<R: Runtime> Client for TorClient<R> {
59    fn isolated_client(&self) -> Arc<dyn rpc::Object> {
60        Arc::new(TorClient::isolated_client(self))
61    }
62
63    fn upcast_arc(self: Arc<Self>) -> Arc<dyn rpc::Object> {
64        self
65    }
66}
67
68impl RpcSession {
69    /// Create a new session object containing a single client object.
70    pub fn new_with_client<R: Runtime>(client: Arc<arti_client::TorClient<R>>) -> Arc<Self> {
71        Arc::new(Self { client })
72    }
73
74    /// Return a view of the client associated with this session, as an `Arc<dyn
75    /// rpc::Object>.`
76    fn client_as_object(&self) -> Arc<dyn rpc::Object> {
77        self.client.clone().upcast_arc()
78    }
79}
80
81/// Return the default client for a session.
82///
83/// Allocates a new ObjectID,
84/// but does not create a new underlying client object.
85///
86/// The returned ObjectID is a handle to a `TorClient`.
87#[derive(Debug, serde::Deserialize, serde::Serialize, Deftly)]
88#[derive_deftly(DynMethod)]
89#[deftly(rpc(method_name = "arti:get_client"))]
90struct GetClient {}
91
92impl rpc::RpcMethod for GetClient {
93    type Output = rpc::SingleIdResponse;
94    type Update = rpc::NoUpdates;
95}
96
97/// Implement GetClient on an RpcSession.
98async fn get_client_on_session(
99    session: Arc<RpcSession>,
100    _method: Box<GetClient>,
101    ctx: Arc<dyn rpc::Context>,
102) -> Result<rpc::SingleIdResponse, rpc::RpcError> {
103    Ok(rpc::SingleIdResponse::from(
104        ctx.register_owned(session.client.clone().upcast_arc()),
105    ))
106}
107
108/// Implement IsolatedClient on an RpcSession.
109async fn isolated_client_on_session(
110    session: Arc<RpcSession>,
111    _method: Box<arti_client::rpc::IsolatedClient>,
112    ctx: Arc<dyn rpc::Context>,
113) -> Result<rpc::SingleIdResponse, rpc::RpcError> {
114    let new_client = session.client.isolated_client();
115    Ok(rpc::SingleIdResponse::from(ctx.register_owned(new_client)))
116}
117
118/// Implement ConnectWithPrefs on an RpcSession
119///
120/// (Delegates to TorClient.)
121async fn session_connect_with_prefs(
122    session: Arc<RpcSession>,
123    method: Box<ConnectWithPrefs>,
124    ctx: Arc<dyn rpc::Context>,
125) -> ClientConnectionResult<arti_client::DataStream> {
126    *rpc::invoke_special_method(ctx, session.client_as_object(), method)
127        .await
128        .map_err(|e| Box::new(into_internal!("unable to delegate to TorClient")(e)) as _)?
129}
130
131/// Implement ResolveWithPrefs on an RpcSession
132///
133/// (Delegates to TorClient.)
134async fn session_resolve_with_prefs(
135    session: Arc<RpcSession>,
136    method: Box<ResolveWithPrefs>,
137    ctx: Arc<dyn rpc::Context>,
138) -> ClientConnectionResult<Vec<IpAddr>> {
139    *rpc::invoke_special_method(ctx, session.client_as_object(), method)
140        .await
141        .map_err(|e| Box::new(into_internal!("unable to delegate to TorClient")(e)) as _)?
142}
143
144/// Implement ResolvePtrWithPrefs on an RpcSession
145///
146/// (Delegates to TorClient.)
147async fn session_resolve_ptr_with_prefs(
148    session: Arc<RpcSession>,
149    method: Box<ResolvePtrWithPrefs>,
150    ctx: Arc<dyn rpc::Context>,
151) -> ClientConnectionResult<Vec<String>> {
152    *rpc::invoke_special_method(ctx, session.client_as_object(), method)
153        .await
154        .map_err(|e| Box::new(into_internal!("unable to delegate to TorClient")(e)) as _)?
155}
156static_rpc_invoke_fn! {
157    get_client_on_session;
158    isolated_client_on_session;
159    @special session_connect_with_prefs;
160    @special session_resolve_with_prefs;
161    @special session_resolve_ptr_with_prefs;
162}
163
164#[cfg(feature = "describe-methods")]
165mod list_all_methods {
166    use std::{convert::Infallible, sync::Arc};
167
168    use derive_deftly::Deftly;
169    use tor_rpcbase::{self as rpc, static_rpc_invoke_fn, templates::*, RpcDispatchInformation};
170
171    /// Return a description of all recognized RPC methods.
172    ///
173    /// Note that not every recognized method is necessarily invocable in practice.
174    /// Depending on the session's access level, you might not be able to
175    /// access any objects that the method might be invocable upon.
176    ///
177    /// **This is an experimental method.**
178    /// Methods starting with "x_" are extra-unstable.
179    /// See [`RpcDispatchInformation`] for caveats about type names.
180    #[derive(Debug, serde::Deserialize, Deftly)]
181    #[derive_deftly(DynMethod)]
182    #[deftly(rpc(method_name = "arti:x_list_all_rpc_methods"))]
183    struct ListAllRpcMethods {}
184
185    impl rpc::RpcMethod for ListAllRpcMethods {
186        type Output = RpcDispatchInformation;
187        type Update = rpc::NoUpdates;
188    }
189
190    /// Implement ListAllRpcMethods on an RpcSession.
191    async fn session_list_all_rpc_methods(
192        _session: Arc<super::RpcSession>,
193        _method: Box<ListAllRpcMethods>,
194        ctx: Arc<dyn rpc::Context>,
195    ) -> Result<RpcDispatchInformation, Infallible> {
196        Ok(ctx
197            .dispatch_table()
198            .read()
199            .expect("poisoned lock")
200            .dispatch_information())
201    }
202
203    static_rpc_invoke_fn! { session_list_all_rpc_methods; }
204}