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
        // TODO RPC: This relies (somewhat) on deduplication properties for register_owned.
105
        ctx.register_owned(session.client.clone().upcast_arc()),
106
    ))
107
}
108

            
109
/// Implement IsolatedClient on an RpcSession.
110
async fn isolated_client_on_session(
111
    session: Arc<RpcSession>,
112
    _method: Box<arti_client::rpc::IsolatedClient>,
113
    ctx: Arc<dyn rpc::Context>,
114
) -> Result<rpc::SingleIdResponse, rpc::RpcError> {
115
    let new_client = session.client.isolated_client();
116
    Ok(rpc::SingleIdResponse::from(ctx.register_owned(new_client)))
117
}
118

            
119
/// Implement ConnectWithPrefs on an RpcSession
120
///
121
/// (Delegates to TorClient.)
122
async fn session_connect_with_prefs(
123
    session: Arc<RpcSession>,
124
    method: Box<ConnectWithPrefs>,
125
    ctx: Arc<dyn rpc::Context>,
126
) -> ClientConnectionResult<arti_client::DataStream> {
127
    *rpc::invoke_special_method(ctx, session.client_as_object(), method)
128
        .await
129
        .map_err(|e| Box::new(into_internal!("unable to delegate to TorClient")(e)) as _)?
130
}
131

            
132
/// Implement ResolveWithPrefs on an RpcSession
133
///
134
/// (Delegates to TorClient.)
135
async fn session_resolve_with_prefs(
136
    session: Arc<RpcSession>,
137
    method: Box<ResolveWithPrefs>,
138
    ctx: Arc<dyn rpc::Context>,
139
) -> ClientConnectionResult<Vec<IpAddr>> {
140
    *rpc::invoke_special_method(ctx, session.client_as_object(), method)
141
        .await
142
        .map_err(|e| Box::new(into_internal!("unable to delegate to TorClient")(e)) as _)?
143
}
144

            
145
/// Implement ResolvePtrWithPrefs on an RpcSession
146
///
147
/// (Delegates to TorClient.)
148
async fn session_resolve_ptr_with_prefs(
149
    session: Arc<RpcSession>,
150
    method: Box<ResolvePtrWithPrefs>,
151
    ctx: Arc<dyn rpc::Context>,
152
) -> ClientConnectionResult<Vec<String>> {
153
    *rpc::invoke_special_method(ctx, session.client_as_object(), method)
154
        .await
155
        .map_err(|e| Box::new(into_internal!("unable to delegate to TorClient")(e)) as _)?
156
}
157
static_rpc_invoke_fn! {
158
    get_client_on_session;
159
    isolated_client_on_session;
160
    @special session_connect_with_prefs;
161
    @special session_resolve_with_prefs;
162
    @special session_resolve_ptr_with_prefs;
163
}
164

            
165
#[cfg(feature = "describe-methods")]
166
mod list_all_methods {
167
    use std::{convert::Infallible, sync::Arc};
168

            
169
    use derive_deftly::Deftly;
170
    use tor_rpcbase::{self as rpc, static_rpc_invoke_fn, templates::*, RpcDispatchInformation};
171

            
172
    /// Return a description of all recognized RPC methods.
173
    ///
174
    /// Note that not every recognized method is necessarily invocable in practice.
175
    /// Depending on the session's access level, you might not be able to
176
    /// access any objects that the method might be invocable upon.
177
    ///
178
    /// **This is an experimental method.**
179
    /// Methods starting with "x_" are extra-unstable.
180
    /// See [`RpcDispatchInformation`] for caveats about type names.
181
    #[derive(Debug, serde::Deserialize, Deftly)]
182
    #[derive_deftly(DynMethod)]
183
    #[deftly(rpc(method_name = "arti:x_list_all_rpc_methods"))]
184
    struct ListAllRpcMethods {}
185

            
186
    impl rpc::RpcMethod for ListAllRpcMethods {
187
        type Output = RpcDispatchInformation;
188
        type Update = rpc::NoUpdates;
189
    }
190

            
191
    /// Implement ListAllRpcMethods on an RpcSession.
192
    async fn session_list_all_rpc_methods(
193
        _session: Arc<super::RpcSession>,
194
        _method: Box<ListAllRpcMethods>,
195
        ctx: Arc<dyn rpc::Context>,
196
    ) -> Result<RpcDispatchInformation, Infallible> {
197
        Ok(ctx
198
            .dispatch_table()
199
            .read()
200
            .expect("poisoned lock")
201
            .dispatch_information())
202
    }
203

            
204
    static_rpc_invoke_fn! { session_list_all_rpc_methods; }
205
}