RPC: Implement a "global identifier" for non-session-bound IDs
These identifiers are actually only "global" with respect to a given `RpcMgr`, but they should not be forgeable or reusable across RpcMgr objects. We're going to use them so that we have a kind of identifier for `TorClient`s that we can expose to SOCKS.
This commit is contained in:
parent
d6944bf003
commit
71e98b1a12
|
@ -250,15 +250,18 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tiny-keccak",
|
||||||
"tor-async-utils",
|
"tor-async-utils",
|
||||||
"tor-basic-utils",
|
"tor-basic-utils",
|
||||||
"tor-bytes",
|
"tor-bytes",
|
||||||
"tor-error",
|
"tor-error",
|
||||||
|
"tor-llcrypto",
|
||||||
"tor-rpcbase",
|
"tor-rpcbase",
|
||||||
"tor-rtcompat",
|
"tor-rtcompat",
|
||||||
"tracing",
|
"tracing",
|
||||||
"typetag",
|
"typetag",
|
||||||
"weak-table",
|
"weak-table",
|
||||||
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -844,6 +847,12 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crunchy"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
@ -3572,6 +3581,15 @@ dependencies = [
|
||||||
"time-core",
|
"time-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-keccak"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||||
|
dependencies = [
|
||||||
|
"crunchy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinystr"
|
name = "tinystr"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
|
|
@ -13,7 +13,14 @@ repository = "https://gitlab.torproject.org/tpo/core/arti.git/"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
full = ["arti-client/full", "tor-async-utils/full", "tor-error/full", "tor-rpcbase/full", "tor-rtcompat/full", "tor-bytes/full"]
|
full = [
|
||||||
|
"arti-client/full",
|
||||||
|
"tor-async-utils/full",
|
||||||
|
"tor-error/full",
|
||||||
|
"tor-rpcbase/full",
|
||||||
|
"tor-rtcompat/full",
|
||||||
|
"tor-bytes/full",
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
arti-client = { path = "../arti-client", version = "0.9.1", features = ["rpc"] }
|
arti-client = { path = "../arti-client", version = "0.9.1", features = ["rpc"] }
|
||||||
|
@ -29,14 +36,17 @@ rand = "0.8"
|
||||||
serde = { version = "1.0.103", features = ["derive"] }
|
serde = { version = "1.0.103", features = ["derive"] }
|
||||||
serde_json = "1.0.50"
|
serde_json = "1.0.50"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
|
tiny-keccak = { version = "2", features = ["k12"] }
|
||||||
tor-async-utils = { path = "../tor-async-utils", version = "0.1.1" }
|
tor-async-utils = { path = "../tor-async-utils", version = "0.1.1" }
|
||||||
tor-bytes = { path = "../tor-bytes", version = "0.7.1" }
|
tor-bytes = { path = "../tor-bytes", version = "0.7.1" }
|
||||||
tor-error = { path = "../tor-error", version = "0.5.1" }
|
tor-error = { path = "../tor-error", version = "0.5.1" }
|
||||||
|
tor-llcrypto = { path = "../tor-llcrypto", version = "0.5.1" }
|
||||||
tor-rpcbase = { path = "../tor-rpcbase", version = "0.1.1" }
|
tor-rpcbase = { path = "../tor-rpcbase", version = "0.1.1" }
|
||||||
tor-rtcompat = { path = "../tor-rtcompat", version = "0.9.1" }
|
tor-rtcompat = { path = "../tor-rtcompat", version = "0.9.1" }
|
||||||
tracing = "0.1.36"
|
tracing = "0.1.36"
|
||||||
typetag = "0.2.7"
|
typetag = "0.2.7"
|
||||||
weak-table = "0.3.0"
|
weak-table = "0.3.0"
|
||||||
|
zeroize = "1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
futures-await-test = "0.3.0"
|
futures-await-test = "0.3.0"
|
||||||
|
|
|
@ -86,9 +86,27 @@ pub(crate) type BoxedResponseSink =
|
||||||
Pin<Box<dyn Sink<BoxedResponse, Error = asynchronous_codec::JsonCodecError> + Send>>;
|
Pin<Box<dyn Sink<BoxedResponse, Error = asynchronous_codec::JsonCodecError> + Send>>;
|
||||||
|
|
||||||
/// A random value used to identify an connection.
|
/// A random value used to identify an connection.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, derive_more::From, derive_more::Into)]
|
#[derive(
|
||||||
|
Copy,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Hash,
|
||||||
|
derive_more::From,
|
||||||
|
derive_more::Into,
|
||||||
|
derive_more::AsRef,
|
||||||
|
)]
|
||||||
|
|
||||||
|
// TODO RPC: Document this, and make it participate in the Reader/Writer API
|
||||||
|
// enough that we can stop referring to its internals elsewhere.
|
||||||
pub(crate) struct ConnectionId([u8; 16]);
|
pub(crate) struct ConnectionId([u8; 16]);
|
||||||
|
|
||||||
|
impl ConnectionId {
|
||||||
|
/// The length of a ConnectionId.
|
||||||
|
pub(crate) const LEN: usize = 16;
|
||||||
|
}
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
/// Create a new connection.
|
/// Create a new connection.
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
|
@ -112,18 +130,23 @@ impl Connection {
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
id: &rpc::ObjectId,
|
id: &rpc::ObjectId,
|
||||||
) -> Result<Arc<dyn rpc::Object>, rpc::LookupError> {
|
) -> Result<Arc<dyn rpc::Object>, rpc::LookupError> {
|
||||||
let inner = self.inner.lock().expect("lock poisoned");
|
|
||||||
|
|
||||||
if id.as_ref() == "connection" {
|
if id.as_ref() == "connection" {
|
||||||
Ok(self.clone())
|
Ok(self.clone())
|
||||||
} else {
|
} else {
|
||||||
inner
|
self.lookup_by_idx(crate::objmap::GenIdx::try_decode(id)?)
|
||||||
.objects
|
|
||||||
.lookup(crate::objmap::GenIdx::try_decode(id)?)
|
|
||||||
.ok_or(rpc::LookupError::NoObject(id.clone()))
|
.ok_or(rpc::LookupError::NoObject(id.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// As `lookup_object`, but expect a `GenIdx`.
|
||||||
|
pub(crate) fn lookup_by_idx(
|
||||||
|
self: &Arc<Self>,
|
||||||
|
idx: crate::objmap::GenIdx,
|
||||||
|
) -> Option<Arc<dyn rpc::Object>> {
|
||||||
|
let inner = self.inner.lock().expect("lock poisoned");
|
||||||
|
inner.objects.lookup(idx)
|
||||||
|
}
|
||||||
|
|
||||||
/// Un-register the request `id` and stop tracking its information.
|
/// Un-register the request `id` and stop tracking its information.
|
||||||
fn remove_request(&self, id: &RequestId) {
|
fn remove_request(&self, id: &RequestId) {
|
||||||
let mut inner = self.inner.lock().expect("lock poisoned");
|
let mut inner = self.inner.lock().expect("lock poisoned");
|
||||||
|
|
|
@ -0,0 +1,226 @@
|
||||||
|
//! Manager-global identifiers, for things that need to be identified outside
|
||||||
|
//! the scope of a single RPC connection.
|
||||||
|
//!
|
||||||
|
//! We expect to use this code to identify `TorClient`s and similar objects that
|
||||||
|
//! can be passed as the target of a SOCKS request. Since the SOCKS request is
|
||||||
|
//! not part of the RPC session, we need a way for it to refer to these objects.
|
||||||
|
|
||||||
|
#![allow(dead_code)] // XXXXXXX
|
||||||
|
|
||||||
|
use tor_bytes::Reader;
|
||||||
|
use tor_llcrypto::util::ct::CtByteArray;
|
||||||
|
use tor_rpcbase::{LookupError, ObjectId};
|
||||||
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
|
use crate::{connection::ConnectionId, objmap::GenIdx};
|
||||||
|
|
||||||
|
/// A [RpcMgr](crate::RpcMgr)-scoped identifier for an RPC object.
|
||||||
|
///
|
||||||
|
/// A `GlobalId` identifies an RPC object uniquely among all the objects visible
|
||||||
|
/// to any active session on an RpcMgr.
|
||||||
|
///
|
||||||
|
/// Its encoding is unforgeable.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub(crate) struct GlobalId {
|
||||||
|
/// The RPC connection within whose object map `local_id` is visible.
|
||||||
|
pub(crate) connection: ConnectionId,
|
||||||
|
/// The identifier of the object within `connection`'s object map.
|
||||||
|
pub(crate) local_id: GenIdx,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The number of bytes in our [`MacKey`].
|
||||||
|
///
|
||||||
|
/// (Our choice of algorithm allows any key length we want; 128 bits should be
|
||||||
|
/// secure enough.)
|
||||||
|
const MAC_KEY_LEN: usize = 16;
|
||||||
|
/// The number of bytes in a [`Mac`].
|
||||||
|
///
|
||||||
|
/// (Our choice of algorithm allows any MAC length we want; 128 bits should be
|
||||||
|
/// enough to make the results unforgeable.)
|
||||||
|
const MAC_LEN: usize = 16;
|
||||||
|
|
||||||
|
/// An key that we use to compute message authentication codes (MACs) for our
|
||||||
|
/// [`GlobalId`]s
|
||||||
|
///
|
||||||
|
/// We do not guarantee any particular MAC algorithm; we should be able to
|
||||||
|
/// change MAC algorithms without breaking any user code. Right now, we choose a
|
||||||
|
/// Kangaroo12-based construction in order to be reasonably fast.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct MacKey {
|
||||||
|
/// The key itself.
|
||||||
|
key: Zeroizing<[u8; MAC_KEY_LEN]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A message authentication code produced by [`MacKey::mac`].
|
||||||
|
type Mac = CtByteArray<MAC_LEN>;
|
||||||
|
|
||||||
|
impl MacKey {
|
||||||
|
/// Construct a new random `MacKey`.
|
||||||
|
pub(crate) fn new<Rng: rand::Rng + rand::CryptoRng>(rng: &mut Rng) -> Self {
|
||||||
|
Self {
|
||||||
|
key: Zeroizing::new(rng.gen()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the AMC of a given input `inp`, and store the result into `out`.
|
||||||
|
///
|
||||||
|
/// The current construction allows `out` to be any length.
|
||||||
|
fn mac(&self, inp: &[u8], out: &mut [u8]) {
|
||||||
|
use tiny_keccak::{Hasher as _, KangarooTwelve as K12};
|
||||||
|
// This is the HopMAC construction from draft-irtf-cfrg-kangarootwelve-10:
|
||||||
|
//
|
||||||
|
// HopMAC(Key, M, C, L) = K12(Key, K12(M, C, 32), L)
|
||||||
|
//
|
||||||
|
// TODO RPC: Just use KMAC or something.
|
||||||
|
let mut inner = K12::new(b"artirpc globalid");
|
||||||
|
let mut hash = [0_u8; 32];
|
||||||
|
inner.update(inp);
|
||||||
|
inner.finalize(&mut hash);
|
||||||
|
|
||||||
|
let mut outer = K12::new(hash);
|
||||||
|
outer.update(&self.key[..]);
|
||||||
|
outer.finalize(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlobalId {
|
||||||
|
/// The number of bytes used to encode a `GlobalId` in binary form.
|
||||||
|
const ENCODED_LEN: usize = MAC_LEN + ConnectionId::LEN + GenIdx::BYTE_LEN;
|
||||||
|
|
||||||
|
/// Encode this ID in an unforgeable string that we can later use to
|
||||||
|
/// uniquely identify an RPC object.
|
||||||
|
///
|
||||||
|
/// As with local IDs, this encoding is nondeterministic.
|
||||||
|
pub(crate) fn encode(&self, key: &MacKey) -> ObjectId {
|
||||||
|
use base64ct::{Base64Unpadded as B64, Encoding};
|
||||||
|
let bytes = self.encode_as_bytes(key, &mut rand::thread_rng());
|
||||||
|
B64::encode_string(&bytes[..]).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// As `encode`, but do not base64-encode the result.
|
||||||
|
fn encode_as_bytes<R: rand::RngCore>(&self, key: &MacKey, rng: &mut R) -> Vec<u8> {
|
||||||
|
let mut bytes = Vec::with_capacity(Self::ENCODED_LEN);
|
||||||
|
bytes.resize(MAC_LEN, 0);
|
||||||
|
bytes.extend_from_slice(self.connection.as_ref());
|
||||||
|
bytes.extend_from_slice(&self.local_id.to_bytes(rng));
|
||||||
|
{
|
||||||
|
// TODO RPC: Maybe we should stick the MAC at the end to make everything simpler.
|
||||||
|
let (mac, text) = bytes.split_at_mut(MAC_LEN);
|
||||||
|
key.mac(text, mac);
|
||||||
|
}
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to decode and validate `s` as a [`GlobalId`].
|
||||||
|
pub(crate) fn try_decode(key: &MacKey, s: &ObjectId) -> Result<Self, LookupError> {
|
||||||
|
use base64ct::{Base64Unpadded as B64, Encoding};
|
||||||
|
let mut bytes = [0_u8; Self::ENCODED_LEN];
|
||||||
|
let byte_slice = B64::decode(s.as_ref(), &mut bytes[..])
|
||||||
|
.map_err(|_| LookupError::NoObject(s.clone()))?;
|
||||||
|
Self::try_decode_from_bytes(key, byte_slice).ok_or_else(|| LookupError::NoObject(s.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// As `try_decode`, but expect a byte slice rather than a base64-encoded string.
|
||||||
|
fn try_decode_from_bytes(key: &MacKey, bytes: &[u8]) -> Option<Self> {
|
||||||
|
if bytes.len() != Self::ENCODED_LEN {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO RPC: Just use Reader here?
|
||||||
|
|
||||||
|
let mut found_mac = [0; MAC_LEN];
|
||||||
|
key.mac(&bytes[MAC_LEN..], &mut found_mac[..]);
|
||||||
|
let found_mac = Mac::from(found_mac);
|
||||||
|
|
||||||
|
let mut r: Reader = Reader::from_slice(bytes);
|
||||||
|
let declared_mac: Mac = r.extract().ok()?;
|
||||||
|
if found_mac != declared_mac {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let connection = r.extract::<[u8; ConnectionId::LEN]>().ok()?.into();
|
||||||
|
let rest = r.into_rest();
|
||||||
|
let local_id = GenIdx::from_bytes(rest)?;
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
connection,
|
||||||
|
local_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
// @@ begin test lint list maintained by maint/add_warning @@
|
||||||
|
#![allow(clippy::bool_assert_comparison)]
|
||||||
|
#![allow(clippy::clone_on_copy)]
|
||||||
|
#![allow(clippy::dbg_macro)]
|
||||||
|
#![allow(clippy::print_stderr)]
|
||||||
|
#![allow(clippy::print_stdout)]
|
||||||
|
#![allow(clippy::single_char_pattern)]
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::unchecked_duration_subtraction)]
|
||||||
|
//! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roundtrip() {
|
||||||
|
use generational_arena as ga;
|
||||||
|
let mut rng = tor_basic_utils::test_rng::testing_rng();
|
||||||
|
|
||||||
|
let conn1 = ConnectionId::from(*b"example1-------!");
|
||||||
|
let conn2 = ConnectionId::from(*b"example2!!!!!!!!");
|
||||||
|
let genidx_s1 = GenIdx::Strong(ga::Index::from_raw_parts(42, 42));
|
||||||
|
let genidx_w2 = GenIdx::Weak(ga::Index::from_raw_parts(172, 171));
|
||||||
|
|
||||||
|
let gid1 = GlobalId {
|
||||||
|
connection: conn1,
|
||||||
|
local_id: genidx_s1,
|
||||||
|
};
|
||||||
|
let gid2 = GlobalId {
|
||||||
|
connection: conn2,
|
||||||
|
local_id: genidx_w2,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mac_key = MacKey::new(&mut rng);
|
||||||
|
let enc1 = gid1.encode(&mac_key);
|
||||||
|
let gid1_decoded = GlobalId::try_decode(&mac_key, &enc1).unwrap();
|
||||||
|
assert_eq!(gid1, gid1_decoded);
|
||||||
|
|
||||||
|
let enc2 = gid2.encode(&mac_key);
|
||||||
|
let gid2_decoded = GlobalId::try_decode(&mac_key, &enc2).unwrap();
|
||||||
|
assert_eq!(gid2, gid2_decoded);
|
||||||
|
assert_ne!(gid1_decoded, gid2_decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mac_works() {
|
||||||
|
use generational_arena as ga;
|
||||||
|
let mut rng = tor_basic_utils::test_rng::testing_rng();
|
||||||
|
|
||||||
|
let conn1 = ConnectionId::from(*b"example1-------!");
|
||||||
|
let conn2 = ConnectionId::from(*b"example2!!!!!!!!");
|
||||||
|
let genidx_s1 = GenIdx::Strong(ga::Index::from_raw_parts(42, 42));
|
||||||
|
let genidx_w1 = GenIdx::Weak(ga::Index::from_raw_parts(172, 171));
|
||||||
|
|
||||||
|
let gid1 = GlobalId {
|
||||||
|
connection: conn1,
|
||||||
|
local_id: genidx_s1,
|
||||||
|
};
|
||||||
|
let gid2 = GlobalId {
|
||||||
|
connection: conn2,
|
||||||
|
local_id: genidx_w1,
|
||||||
|
};
|
||||||
|
let mac_key = MacKey::new(&mut rng);
|
||||||
|
let enc1 = gid1.encode_as_bytes(&mac_key, &mut rng);
|
||||||
|
let enc2 = gid2.encode_as_bytes(&mac_key, &mut rng);
|
||||||
|
|
||||||
|
// Make a 'combined' encoded gid with the mac from one and the info from
|
||||||
|
// the other.
|
||||||
|
let mut combined = Vec::from(&enc1[0..MAC_LEN]);
|
||||||
|
combined.extend_from_slice(&enc2[MAC_LEN..]);
|
||||||
|
let outcome = GlobalId::try_decode_from_bytes(&mac_key, &combined[..]);
|
||||||
|
// Can't decode, because MAC was wrong.
|
||||||
|
assert!(outcome.is_none());
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,7 @@
|
||||||
mod cancel;
|
mod cancel;
|
||||||
mod connection;
|
mod connection;
|
||||||
mod err;
|
mod err;
|
||||||
|
mod globalid;
|
||||||
mod mgr;
|
mod mgr;
|
||||||
mod msgs;
|
mod msgs;
|
||||||
mod objmap;
|
mod objmap;
|
||||||
|
|
|
@ -8,7 +8,10 @@ use tor_rpcbase as rpc;
|
||||||
use tor_rtcompat::Runtime;
|
use tor_rtcompat::Runtime;
|
||||||
use weak_table::WeakValueHashMap;
|
use weak_table::WeakValueHashMap;
|
||||||
|
|
||||||
use crate::connection::{Connection, ConnectionId};
|
use crate::{
|
||||||
|
connection::{Connection, ConnectionId},
|
||||||
|
globalid::{GlobalId, MacKey},
|
||||||
|
};
|
||||||
|
|
||||||
/// Shared state, configuration, and data for all RPC sessions.
|
/// Shared state, configuration, and data for all RPC sessions.
|
||||||
///
|
///
|
||||||
|
@ -16,6 +19,11 @@ use crate::connection::{Connection, ConnectionId};
|
||||||
///
|
///
|
||||||
/// TODO RPC: Actually not all of the above functionality is implemented yet. But it should be.
|
/// TODO RPC: Actually not all of the above functionality is implemented yet. But it should be.
|
||||||
pub struct RpcMgr {
|
pub struct RpcMgr {
|
||||||
|
/// A key that we use to ensure that identifiers are unforgeable.
|
||||||
|
///
|
||||||
|
/// When giving out a global identifier.
|
||||||
|
mac_key: MacKey,
|
||||||
|
|
||||||
/// Lock-protected view of the manager's state.
|
/// Lock-protected view of the manager's state.
|
||||||
//
|
//
|
||||||
// TODO RPC: We should probably move everything into Inner, and move an Arc
|
// TODO RPC: We should probably move everything into Inner, and move an Arc
|
||||||
|
@ -48,6 +56,7 @@ impl RpcMgr {
|
||||||
#[allow(clippy::new_without_default)]
|
#[allow(clippy::new_without_default)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
RpcMgr {
|
RpcMgr {
|
||||||
|
mac_key: MacKey::new(&mut rand::thread_rng()),
|
||||||
inner: Mutex::new(Inner {
|
inner: Mutex::new(Inner {
|
||||||
dispatch_table: Arc::new(rpc::DispatchTable::from_inventory()),
|
dispatch_table: Arc::new(rpc::DispatchTable::from_inventory()),
|
||||||
connections: WeakValueHashMap::new(),
|
connections: WeakValueHashMap::new(),
|
||||||
|
@ -81,4 +90,26 @@ impl RpcMgr {
|
||||||
);
|
);
|
||||||
connection
|
connection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Look up an object in the context of this `RpcMgr`.
|
||||||
|
///
|
||||||
|
/// Some object identifiers exist in a manager-global context, so that they
|
||||||
|
/// can be used outside of a single RPC session. This function looks up an
|
||||||
|
/// object by such an identifier string. It returns an error if the
|
||||||
|
/// identifier is invalid or the object does not exist.
|
||||||
|
pub fn lookup_object(
|
||||||
|
&self,
|
||||||
|
id: &rpc::ObjectId,
|
||||||
|
) -> Result<Arc<dyn rpc::Object>, rpc::LookupError> {
|
||||||
|
let global_id = GlobalId::try_decode(&self.mac_key, id)?;
|
||||||
|
self.lookup_by_global_id(&global_id)
|
||||||
|
.ok_or_else(|| rpc::LookupError::NoObject(id.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// As `lookup_object`, but takes a parsed and validated [`GlobalId`].
|
||||||
|
pub(crate) fn lookup_by_global_id(&self, id: &GlobalId) -> Option<Arc<dyn rpc::Object>> {
|
||||||
|
let inner = self.inner.lock().expect("lock poisoned");
|
||||||
|
let connection = inner.connections.get(&id.connection)?;
|
||||||
|
connection.lookup_by_idx(id.local_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue