1
//! Declare the RPC session object as exposed from the RPC server run by the `arti` crate.
2

            
3
use arti_client::TorClient;
4
use arti_rpcserver::RpcAuthentication;
5
use derive_deftly::Deftly;
6
use futures::stream::StreamExt as _;
7
use std::sync::Arc;
8
use tor_async_utils::{DropNotifyEofSignallable, DropNotifyWatchSender};
9
use tor_rpcbase::{self as rpc};
10
use tor_rtcompat::Runtime;
11

            
12
use crate::proxy::port_info;
13

            
14
use super::proxyinfo::{self, ProxyInfo};
15

            
16
/// A top-level RPC session object.
17
///
18
/// This is the first object that an RPC user receives upon authenticating;
19
/// It is returned by `auth:authenticate`.
20
///
21
/// Other objects (`TorClient`,`RpcDataStream`, etc)
22
/// are available using methods on this object.
23
/// (See the list of available methods.)
24
///
25
/// This type wraps and delegates to [`arti_rpcserver::RpcSession`],
26
/// but exposes additional functionality not available at the
27
/// level of [`arti_rpcserver`], including information about configured proxies.
28
///
29
/// This ObjectID for this object can be used as the target of a SOCKS stream.
30
#[derive(Deftly)]
31
#[derive_deftly(rpc::Object)]
32
#[deftly(rpc(
33
    delegate_with = "|this: &Self| Some(this.session.clone())",
34
    delegate_type = "arti_rpcserver::RpcSession"
35
))]
36
#[deftly(rpc(expose_outside_of_session))]
37
pub(super) struct ArtiRpcSession {
38
    /// State about the `arti` server, as seen by the Rpc system.
39
    pub(super) arti_state: Arc<RpcVisibleArtiState>,
40
    /// The underlying RpcSession object that we delegate to.
41
    session: Arc<arti_rpcserver::RpcSession>,
42
}
43

            
44
/// Information about the current global top-level Arti state,
45
/// as exposed to an Rpc Session.
46
//
47
// TODO: This type is dangerously close to being a collection of globals.
48
// We should refactor it aggressively when we refactor the `arti` crate.
49
//
50
// TODO: Right now this is constructed in the same form that it's used in
51
// ArtiRpcSession.  Later on, we could split it into one type that
52
// the rest of this crate constructs, and another type that the
53
// ArtiRpcSession actually uses. We should do that if the needs seem to diverge.
54
pub(crate) struct RpcVisibleArtiState {
55
    /// A `ProxyInfo` that we hand out when asked to list our proxy ports.
56
    ///
57
    /// Right now it only lists Socks; in the future it may list more.
58
    proxy_info: postage::watch::Receiver<ProxyInfoState>,
59
}
60

            
61
/// Handle to set RPC state across RPC sessions.  (See `RpcVisibleArtiState`.)
62
#[derive(Debug)]
63
pub(crate) struct RpcStateSender {
64
    /// Sender for setting our list of proxy ports.
65
    proxy_info_sender: DropNotifyWatchSender<ProxyInfoState>,
66
}
67

            
68
impl ArtiRpcSession {
69
    /// Construct a new `ArtiRpcSession`.
70
    ///
71
    /// Privileges on the session (if any) are derived from `auth`, which describes
72
    /// how the user authenticated.
73
    ///
74
    /// The session receives a new isolated TorClient, based on `client_root`.
75
    pub(super) fn new<R: Runtime>(
76
        auth: &RpcAuthentication,
77
        client_root: &TorClient<R>,
78
        arti_state: &Arc<RpcVisibleArtiState>,
79
    ) -> Arc<Self> {
80
        let _ = auth; // This is currently unused; any authentication gives the same result.
81
        let client = client_root.isolated_client();
82
        let session = arti_rpcserver::RpcSession::new_with_client(Arc::new(client));
83
        let arti_state = Arc::clone(arti_state);
84
        Arc::new(ArtiRpcSession {
85
            session,
86
            arti_state,
87
        })
88
    }
89
}
90

            
91
/// Possible state for a watched proxy_info.
92
#[derive(Debug, Clone)]
93
enum ProxyInfoState {
94
    /// We haven't set it yet.
95
    Unset,
96
    /// We've set it to a given value.
97
    Set(Arc<ProxyInfo>),
98
    /// The sender has been dropped.
99
    Eof,
100
}
101

            
102
impl DropNotifyEofSignallable for ProxyInfoState {
103
4
    fn eof() -> Self {
104
4
        Self::Eof
105
4
    }
106
}
107

            
108
impl RpcVisibleArtiState {
109
    /// Construct a new `RpcVisibleArtiState`.
110
4
    pub(crate) fn new() -> (Arc<Self>, RpcStateSender) {
111
4
        let (proxy_info_sender, proxy_info) = postage::watch::channel_with(ProxyInfoState::Unset);
112
4
        let proxy_info_sender = DropNotifyWatchSender::new(proxy_info_sender);
113
4
        (
114
4
            Arc::new(Self { proxy_info }),
115
4
            RpcStateSender { proxy_info_sender },
116
4
        )
117
4
    }
118

            
119
    /// Return the latest proxy info, waiting until it is set.
120
    ///
121
    /// Return an error if the sender has been closed.
122
12
    pub(super) async fn get_proxy_info(&self) -> Result<Arc<ProxyInfo>, ()> {
123
8
        let mut proxy_info = self.proxy_info.clone();
124
12
        while let Some(v) = proxy_info.next().await {
125
12
            match v {
126
4
                ProxyInfoState::Unset => {
127
4
                    // Not yet set, try again.
128
4
                }
129
8
                ProxyInfoState::Set(proxyinfo) => return Ok(Arc::clone(&proxyinfo)),
130
                ProxyInfoState::Eof => return Err(()),
131
            }
132
        }
133
        Err(())
134
8
    }
135
}
136

            
137
impl RpcStateSender {
138
    /// Set the list of stream listener addresses on this state.
139
    ///
140
    /// This method may only be called once per state.
141
4
    pub(crate) fn set_stream_listeners(&mut self, ports: &[port_info::Port]) {
142
4
        let info = ProxyInfo {
143
4
            proxies: ports
144
4
                .iter()
145
6
                .filter_map(|port| {
146
                    Some(proxyinfo::Proxy {
147
4
                        listener: proxyinfo::ProxyListener::try_from_portinfo(port)?,
148
                    })
149
4
                })
150
4
                .collect(),
151
        };
152
4
        *self.proxy_info_sender.borrow_mut() = ProxyInfoState::Set(Arc::new(info));
153
4
    }
154
}
155

            
156
#[cfg(test)]
157
mod test {
158
    // @@ begin test lint list maintained by maint/add_warning @@
159
    #![allow(clippy::bool_assert_comparison)]
160
    #![allow(clippy::clone_on_copy)]
161
    #![allow(clippy::dbg_macro)]
162
    #![allow(clippy::mixed_attributes_style)]
163
    #![allow(clippy::print_stderr)]
164
    #![allow(clippy::print_stdout)]
165
    #![allow(clippy::single_char_pattern)]
166
    #![allow(clippy::unwrap_used)]
167
    #![allow(clippy::unchecked_time_subtraction)]
168
    #![allow(clippy::useless_vec)]
169
    #![allow(clippy::needless_pass_by_value)]
170
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
171

            
172
    use tor_rtcompat::SpawnExt as _;
173
    use tor_rtmock::MockRuntime;
174

            
175
    use super::*;
176

            
177
    #[test]
178
    fn set_proxy_info() {
179
        MockRuntime::test_with_various(|rt| async move {
180
            let (state, mut sender) = RpcVisibleArtiState::new();
181
            let _task = rt.clone().spawn_with_handle(async move {
182
                sender.set_stream_listeners(&[port_info::Port {
183
                    protocol: port_info::SupportedProtocol::Socks,
184
                    address: "8.8.8.8:40".parse().unwrap(),
185
                }]);
186
                sender // keep sender alive
187
            });
188

            
189
            let value = state.get_proxy_info().await;
190

            
191
            // At this point, we've returned once, so this will test that we get a fresh answer even
192
            // if we already set the inner value.
193
            let value_again = state.get_proxy_info().await;
194
            assert_eq!(value.unwrap(), value_again.unwrap());
195
        });
196
    }
197
}