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

            
6
use arti_client::{
7
    rpc::{ClientConnectionResult, ConnectWithPrefs, ResolvePtrWithPrefs, ResolveWithPrefs},
8
    TorClient,
9
};
10
use derive_deftly::Deftly;
11
use std::{net::IpAddr, sync::Arc};
12
use tor_error::into_internal;
13
use tor_rtcompat::Runtime;
14

            
15
use 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))]
42
pub 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.
50
trait 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

            
58
impl<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

            
68
impl 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"))]
90
struct GetClient {}
91

            
92
impl rpc::RpcMethod for GetClient {
93
    type Output = rpc::SingleIdResponse;
94
    type Update = rpc::NoUpdates;
95
}
96

            
97
/// Implement GetClient on an RpcSession.
98
async 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.
109
async 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.)
121
async 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.)
134
async 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.)
147
async 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
}
156
static_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")]
165
mod 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
}