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_rtcompat::Runtime;
13

            
14
use tor_rpcbase::{self as rpc, static_rpc_invoke_fn, templates::*};
15

            
16
/// An authenticated RPC session: a capability through which most other RPC functionality is available
17
///
18
/// This relates to [`Connection`](crate::Connection) as follows:
19
///
20
///  * A `Connection` exists prior to authentication;
21
///    whereas an `RpcSession` comes into being as a result of authentication.
22
///
23
///  * The `RpcSession` is principally owned by the `Connection`'s object table.
24
///
25
///  * Typically, after authentication, there is one `RpcSession` for the `Connection`.
26
///    But a client may authenticate more than once; each time produces a new `RpcSession`.
27
#[derive(Deftly)]
28
#[derive_deftly(Object)]
29
#[deftly(rpc(expose_outside_of_session))]
30
pub struct RpcSession {
31
    /// An inner TorClient object that we use to implement remaining
32
    /// functionality.
33
    #[allow(unused)]
34
    client: Arc<dyn Client>,
35
}
36

            
37
/// Type-erased `TorClient`, as used within an RpcSession.
38
trait Client: rpc::Object {
39
    /// Return a new isolated TorClient.
40
    fn isolated_client(&self) -> Arc<dyn rpc::Object>;
41

            
42
    /// Upcast `self` to an rpc::Object.
43
    fn upcast_arc(self: Arc<Self>) -> Arc<dyn rpc::Object>;
44
}
45

            
46
impl<R: Runtime> Client for TorClient<R> {
47
    fn isolated_client(&self) -> Arc<dyn rpc::Object> {
48
        Arc::new(TorClient::isolated_client(self))
49
    }
50

            
51
    fn upcast_arc(self: Arc<Self>) -> Arc<dyn rpc::Object> {
52
        self
53
    }
54
}
55

            
56
impl RpcSession {
57
    /// Create a new session object containing a single client object.
58
    pub fn new_with_client<R: Runtime>(client: Arc<arti_client::TorClient<R>>) -> Arc<Self> {
59
        Arc::new(Self { client })
60
    }
61

            
62
    /// Return a view of the client associated with this session, as an `Arc<dyn
63
    /// rpc::Object>.`
64
    fn client_as_object(&self) -> Arc<dyn rpc::Object> {
65
        self.client.clone().upcast_arc()
66
    }
67
}
68

            
69
/// RPC method to release a single strong reference.
70
#[derive(Debug, serde::Deserialize, Deftly)]
71
#[derive_deftly(DynMethod)]
72
#[deftly(rpc(method_name = "rpc:release"))]
73
struct RpcRelease {
74
    /// The object to release. Must be a strong reference.
75
    ///
76
    /// TODO RPC: Releasing a weak reference is perilous and hard-to-define
77
    /// based on how we have implemented our object ids.  If you tell the objmap
78
    /// to "release" a single name for a weak reference, you are releasing every
79
    /// name for that weak reference, which may have surprising results.
80
    ///
81
    /// This might be a sign of a design problem.
82
    obj: rpc::ObjectId,
83
}
84
/// RPC method to release a single strong reference, creating a weak reference
85
/// in its place.
86
#[derive(Debug, serde::Serialize, serde::Deserialize)]
87
#[allow(unused)] //TODO RPC
88
struct RpcDowngrade {
89
    /// The object to downgrade
90
    obj: rpc::ObjectId,
91
}
92

            
93
impl rpc::RpcMethod for RpcRelease {
94
    type Output = rpc::Nil;
95
    type Update = rpc::NoUpdates;
96
}
97

            
98
/// Implementation for calling "release" on a Session.
99
async fn rpc_release(
100
    _obj: Arc<RpcSession>,
101
    method: Box<RpcRelease>,
102
    ctx: Arc<dyn rpc::Context>,
103
) -> Result<rpc::Nil, rpc::RpcError> {
104
    ctx.release_owned(&method.obj)?;
105
    Ok(rpc::Nil::default())
106
}
107

            
108
/// A simple temporary method to echo a reply.
109
#[derive(Debug, serde::Deserialize, serde::Serialize, Deftly)]
110
#[derive_deftly(DynMethod)]
111
#[deftly(rpc(method_name = "arti:x_echo"))]
112
struct Echo {
113
    /// A message to echo.
114
    msg: String,
115
}
116

            
117
impl rpc::RpcMethod for Echo {
118
    type Output = Echo;
119
    type Update = rpc::NoUpdates;
120
}
121

            
122
/// Implementation for calling "echo" on a Session.
123
///
124
/// TODO RPC: Remove this. It shouldn't exist.
125
async fn echo_on_session(
126
    _obj: Arc<RpcSession>,
127
    method: Box<Echo>,
128
    _ctx: Arc<dyn rpc::Context>,
129
) -> Result<Echo, rpc::RpcError> {
130
    Ok(*method)
131
}
132

            
133
/// An RPC method to return the default client for a session.
134
#[derive(Debug, serde::Deserialize, serde::Serialize, Deftly)]
135
#[derive_deftly(DynMethod)]
136
#[deftly(rpc(method_name = "arti:get_client"))]
137
struct GetClient {}
138

            
139
impl rpc::RpcMethod for GetClient {
140
    type Output = rpc::SingletonId;
141
    type Update = rpc::NoUpdates;
142
}
143

            
144
/// Implement GetClient on an RpcSession.
145
async fn get_client_on_session(
146
    session: Arc<RpcSession>,
147
    _method: Box<GetClient>,
148
    ctx: Arc<dyn rpc::Context>,
149
) -> Result<rpc::SingletonId, rpc::RpcError> {
150
    Ok(rpc::SingletonId::from(
151
        // TODO RPC: This relies (somewhat) on deduplication properties for register_owned.
152
        ctx.register_owned(session.client.clone().upcast_arc()),
153
    ))
154
}
155

            
156
/// Implement IsolatedClient on an RpcSession.
157
async fn isolated_client_on_session(
158
    session: Arc<RpcSession>,
159
    _method: Box<arti_client::rpc::IsolatedClient>,
160
    ctx: Arc<dyn rpc::Context>,
161
) -> Result<rpc::SingletonId, rpc::RpcError> {
162
    let new_client = session.client.isolated_client();
163
    Ok(rpc::SingletonId::from(ctx.register_owned(new_client)))
164
}
165

            
166
/// Implement ConnectWithPrefs on an RpcSession
167
///
168
/// (Delegates to TorClient.)
169
async fn session_connect_with_prefs(
170
    session: Arc<RpcSession>,
171
    method: Box<ConnectWithPrefs>,
172
    ctx: Arc<dyn rpc::Context>,
173
) -> ClientConnectionResult<arti_client::DataStream> {
174
    *rpc::invoke_special_method(ctx, session.client_as_object(), method)
175
        .await
176
        .map_err(|e| Box::new(e) as _)?
177
}
178

            
179
/// Implement ResolveWithPrefs on an RpcSession
180
///
181
/// (Delegates to TorClient.)
182
async fn session_resolve_with_prefs(
183
    session: Arc<RpcSession>,
184
    method: Box<ResolveWithPrefs>,
185
    ctx: Arc<dyn rpc::Context>,
186
) -> ClientConnectionResult<Vec<IpAddr>> {
187
    *rpc::invoke_special_method(ctx, session.client_as_object(), method)
188
        .await
189
        .map_err(|e| Box::new(e) as _)?
190
}
191

            
192
/// Implement ResolvePtrWithPrefs on an RpcSession
193
///
194
/// (Delegates to TorClient.)
195
async fn session_resolve_ptr_with_prefs(
196
    session: Arc<RpcSession>,
197
    method: Box<ResolvePtrWithPrefs>,
198
    ctx: Arc<dyn rpc::Context>,
199
) -> ClientConnectionResult<Vec<String>> {
200
    *rpc::invoke_special_method(ctx, session.client_as_object(), method)
201
        .await
202
        .map_err(|e| Box::new(e) as _)?
203
}
204
static_rpc_invoke_fn! {
205
    rpc_release;
206
    echo_on_session;
207
    get_client_on_session;
208
    isolated_client_on_session;
209
    @special session_connect_with_prefs;
210
    @special session_resolve_with_prefs;
211
    @special session_resolve_ptr_with_prefs;
212
}
213

            
214
#[cfg(feature = "describe-methods")]
215
mod list_all_methods {
216
    use std::{convert::Infallible, sync::Arc};
217

            
218
    use derive_deftly::Deftly;
219
    use tor_rpcbase::{self as rpc, static_rpc_invoke_fn, templates::*, RpcDispatchInformation};
220

            
221
    /// An RPC method to return a description of methods recognized on a session.
222
    ///
223
    /// Note that not every recognized method is necessarily invocable in practice
224
    /// depending on the session's access level, you might not be able to
225
    /// access any objects that the method might be invocable upon.
226
    ///
227
    /// Methods starting with "x_" are extra-unstable.
228
    /// See [`RpcDispatchInformation`] for caveats about type names.
229
    #[derive(Debug, serde::Deserialize, Deftly)]
230
    #[derive_deftly(DynMethod)]
231
    #[deftly(rpc(method_name = "arti:x_list_all_rpc_methods"))]
232
    struct ListAllRpcMethods {}
233

            
234
    impl rpc::RpcMethod for ListAllRpcMethods {
235
        type Output = RpcDispatchInformation;
236
        type Update = rpc::NoUpdates;
237
    }
238

            
239
    /// Implement ListAllRpcMethods on an RpcSession.
240
    async fn session_list_all_rpc_methods(
241
        _session: Arc<super::RpcSession>,
242
        _method: Box<ListAllRpcMethods>,
243
        ctx: Arc<dyn rpc::Context>,
244
    ) -> Result<RpcDispatchInformation, Infallible> {
245
        Ok(ctx
246
            .dispatch_table()
247
            .read()
248
            .expect("poisoned lock")
249
            .dispatch_information())
250
    }
251

            
252
    static_rpc_invoke_fn! { session_list_all_rpc_methods; }
253
}