#![cfg(feature = "rpc")]
use std::mem::ManuallyDrop;
use std::sync::Arc;
use std::thread;
use rsbinder::rpc::transport::MemTransport;
use rsbinder::rpc::{AddressSpace, RpcServer, RpcSession};
use rsbinder::{
Binder, DeathRecipient, IBinder, Interface, Parcel, Remotable, Result, SIBinder, StatusCode,
Transactable, TransactionCode, WIBinder,
};
const DESC: &str = "rsbinder.test.ICrossStack";
struct BnSvc;
impl Remotable for BnSvc {
fn descriptor() -> &'static str {
DESC
}
fn on_transact(&self, _: TransactionCode, _: &mut Parcel, _: &mut Parcel) -> Result<()> {
Err(StatusCode::UnknownTransaction)
}
fn on_dump(&self, _: &mut dyn std::io::Write, _: &[String]) -> Result<()> {
Ok(())
}
}
fn local_root() -> SIBinder {
Interface::as_binder(&Binder::new(BnSvc))
}
struct Pair {
client: RpcSession,
server: Option<RpcSession>,
thread: Option<thread::JoinHandle<()>>,
}
impl Pair {
fn new() -> Self {
let (a, b) = MemTransport::pair();
let server = RpcSession::new(Box::new(a), AddressSpace::Acceptor).expect("server session");
server.set_root(local_root()).expect("set_root");
let serving = server.clone();
let thread = thread::spawn(move || {
let _ = serving.serve_blocking();
});
let client = RpcSession::new(Box::new(b), AddressSpace::Initiator).expect("client session");
Self {
client,
server: Some(server),
thread: Some(thread),
}
}
fn remote_root(&self) -> SIBinder {
self.client.get_root().expect("get_root")
}
}
impl Drop for Pair {
fn drop(&mut self) {
if let Some(s) = self.server.take() {
s.close_session();
}
if let Some(t) = self.thread.take() {
let _ = t.join();
}
}
}
struct KernelishProxy;
impl IBinder for KernelishProxy {
fn link_to_death(&self, _: std::sync::Weak<dyn DeathRecipient>) -> Result<()> {
Err(StatusCode::InvalidOperation)
}
fn unlink_to_death(&self, _: std::sync::Weak<dyn DeathRecipient>) -> Result<()> {
Err(StatusCode::InvalidOperation)
}
fn ping_binder(&self) -> Result<()> {
Ok(())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_transactable(&self) -> Option<&dyn Transactable> {
None
}
fn descriptor(&self) -> &str {
"rsbinder.test.IKernelish"
}
fn is_remote(&self) -> bool {
true
}
fn inc_strong(&self, _: &SIBinder) -> Result<()> {
Ok(())
}
fn attempt_inc_strong(&self) -> bool {
true
}
fn dec_strong(&self, _: Option<ManuallyDrop<SIBinder>>) -> Result<()> {
Ok(())
}
fn inc_weak(&self, _: &WIBinder) -> Result<()> {
Ok(())
}
fn dec_weak(&self) -> Result<()> {
Ok(())
}
}
fn kernelish() -> SIBinder {
SIBinder::new(Arc::new(KernelishProxy)).expect("SIBinder::new")
}
#[test]
fn kernel_parcel_refuses_rpc_proxy() {
let pair = Pair::new();
let proxy = pair.remote_root();
let mut p = Parcel::new();
assert_eq!(
p.write(&proxy).unwrap_err(),
StatusCode::InvalidOperation,
"AC-22.1: an RPC proxy cannot be written into a kernel parcel"
);
let mut p = Parcel::new();
assert_eq!(
p.write(&Some(proxy.clone())).unwrap_err(),
StatusCode::InvalidOperation,
"AC-22.1: Option<SIBinder> takes the same funnel"
);
let mut p = Parcel::new();
assert_eq!(
p.write(&vec![proxy.clone()]).unwrap_err(),
StatusCode::InvalidOperation,
"AC-22.1: Vec<SIBinder> takes the same funnel"
);
let mut p = Parcel::new();
p.write(&None::<SIBinder>)
.expect("a null binder is still writable into a kernel parcel");
}
#[test]
fn local_binder_still_writes_into_an_rpc_parcel() {
let pair = Pair::new();
let remote = pair.remote_root();
let rp = (*remote).as_remote().expect("as_remote");
let mut data = rp.prepare_transact(false).expect("prepare_transact");
data.write(&local_root())
.expect("AC-22.5: a local binder must still cross into an RPC parcel");
}
#[test]
fn rpc_parcel_refuses_kernel_proxy() {
let pair = Pair::new();
let remote = pair.remote_root();
let rp = (*remote).as_remote().expect("as_remote");
let mut data = rp.prepare_transact(false).expect("prepare_transact");
assert_eq!(
data.write(&kernelish()).unwrap_err(),
StatusCode::InvalidOperation,
"AC-22.2: a kernel proxy cannot be written into an RPC parcel"
);
assert_eq!(
pair.client.local_node_count(),
0,
"AC-22.2: the refused binder must not have been registered as a local node"
);
}
#[test]
fn rpc_parcel_refuses_another_sessions_proxy() {
let one = Pair::new();
let two = Pair::new();
let foreign = one.remote_root();
let remote_two = two.remote_root();
let rp = (*remote_two).as_remote().expect("as_remote");
let mut data = rp.prepare_transact(false).expect("prepare_transact");
assert_eq!(
data.write(&foreign).unwrap_err(),
StatusCode::InvalidOperation,
"AC-22.3: a proxy from an unrelated RPC session is refused"
);
let mut data = rp.prepare_transact(false).expect("prepare_transact");
data.write(&remote_two)
.expect("AC-22.3: this session's own proxy may travel back home");
}
#[test]
fn registration_refuses_a_remote_binder() {
let pair = Pair::new();
let proxy = pair.remote_root();
let path = SockPath::new("reg");
let server = RpcServer::setup_unix_server(&path.0).expect("bind");
assert_eq!(
server.set_root(proxy.clone()).unwrap_err(),
StatusCode::InvalidOperation,
"AC-22.4: RpcServer::set_root refuses a remote binder"
);
assert_eq!(
server.add_service("gw", proxy.clone()).unwrap_err(),
StatusCode::InvalidOperation,
"AC-22.4: RpcServer::add_service refuses a remote binder"
);
server.set_root(local_root()).expect("local root accepted");
server
.add_service("gw", local_root())
.expect("local service accepted");
assert_eq!(
server.set_root(kernelish()).unwrap_err(),
StatusCode::InvalidOperation,
"AC-22.4: a kernel proxy is refused at registration too"
);
let (c, _d) = MemTransport::pair();
let session = RpcSession::new(Box::new(c), AddressSpace::Acceptor).expect("session");
assert_eq!(
session.set_root(proxy).unwrap_err(),
StatusCode::InvalidOperation,
"AC-22.4: RpcSession::set_root refuses a remote binder"
);
session.set_root(local_root()).expect("local root accepted");
}
struct SockPath(std::path::PathBuf);
impl SockPath {
fn new(tag: &str) -> Self {
let mut p = std::env::temp_dir();
p.push(format!(
"rsb_xstack_{}_{}_{}.sock",
tag,
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
SockPath(p)
}
}
impl Drop for SockPath {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.0);
}
}
#[test]
fn fixture_root_is_local() {
assert!(
!(*local_root()).is_remote(),
"the fixture root must be local, or every rejection above is vacuous"
);
}