pub mod address;
pub mod fd_mode;
pub(crate) mod lifecycle;
pub mod proxy;
pub mod server;
pub mod session;
pub mod state;
pub mod transport;
pub mod wire;
pub mod wire_android13;
pub use address::{AddressSpace, RpcAddress, SpecialTransaction, RPC_SESSION_ID_NEW};
pub use fd_mode::FileDescriptorTransportMode;
pub use proxy::RpcProxy;
pub use server::RpcServer;
pub use session::RpcSession;
pub use state::RpcState;
pub use transport::{CertId, PeerIdentity, RpcTransport};
#[cfg(feature = "rpc-tls")]
pub use rustls;
pub use wire::{R34Codec, WireCodec, WireMessage, WireReply, WireTransaction};
pub use wire_android13::Android13PlusCodec;
use std::fmt;
pub type RpcResult<T> = std::result::Result<T, RpcError>;
#[non_exhaustive]
#[derive(Debug)]
pub enum RpcError {
PeerClosed,
Truncated,
FrameTooLarge {
declared: usize,
max: usize,
},
Io(std::io::Error),
Protocol(&'static str),
Timeout,
}
impl fmt::Display for RpcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RpcError::PeerClosed => write!(f, "RPC peer closed the connection"),
RpcError::Truncated => write!(f, "RPC frame truncated (incomplete body)"),
RpcError::FrameTooLarge { declared, max } => {
write!(
f,
"RPC frame too large: declared {declared} bytes, max {max}"
)
}
RpcError::Io(e) => write!(f, "RPC transport I/O error: {e}"),
RpcError::Protocol(why) => write!(f, "RPC protocol violation: {why}"),
RpcError::Timeout => write!(f, "RPC wait deadline elapsed"),
}
}
}
impl std::error::Error for RpcError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
RpcError::Io(e) => Some(e),
_ => None,
}
}
}
impl From<std::io::Error> for RpcError {
fn from(e: std::io::Error) -> Self {
use std::io::ErrorKind::*;
match e.kind() {
UnexpectedEof | BrokenPipe | ConnectionReset | ConnectionAborted => {
RpcError::PeerClosed
}
_ => RpcError::Io(e),
}
}
}
impl From<RpcError> for std::io::Error {
fn from(e: RpcError) -> Self {
match e {
RpcError::Io(io) => io,
other => std::io::Error::other(format!("{other}")),
}
}
}
impl From<RpcError> for crate::StatusCode {
fn from(e: RpcError) -> Self {
match e {
RpcError::PeerClosed => crate::StatusCode::DeadObject,
RpcError::Truncated => crate::StatusCode::NotEnoughData,
RpcError::FrameTooLarge { .. } => crate::StatusCode::BadValue,
RpcError::Io(io) => crate::StatusCode::from(io),
RpcError::Protocol(_) => crate::StatusCode::RpcError,
RpcError::Timeout => crate::StatusCode::TimedOut,
}
}
}
#[doc(hidden)]
pub fn __fuzz_decode_rpc_parcel(input: &[u8]) {
use crate::binder::SIBinder;
use crate::error::{Result, StatusCode};
use crate::parcel::{Parcel, RpcParcelOps};
use std::sync::Arc;
struct NullOps;
impl RpcParcelOps for NullOps {
fn write_binder(&self, _b: Option<&SIBinder>, _p: &mut Parcel) -> Result<()> {
Err(StatusCode::DeadObject)
}
fn read_binder(&self, _p: &mut Parcel) -> Result<Option<SIBinder>> {
Err(StatusCode::DeadObject)
}
}
fn fresh(input: &[u8]) -> Parcel {
let mut p = Parcel::from_vec(input.to_vec());
p.set_for_rpc(true);
p.attach_rpc_ops(Arc::new(NullOps));
p.set_data_position(0);
p
}
let _ = fresh(input).read::<i32>();
let _ = fresh(input).read::<i64>();
let _ = fresh(input).read::<String>();
let _ = fresh(input).read::<Vec<i32>>();
let _ = fresh(input).read::<Vec<i64>>();
let _ = fresh(input).read::<Vec<String>>();
let _ = fresh(input).read::<Option<SIBinder>>();
let _ = fresh(input).read::<SIBinder>();
}
#[cfg(test)]
mod tests {
use super::*;
use crate::StatusCode;
#[test]
fn io_clean_close_maps_to_peer_closed() {
for kind in [
std::io::ErrorKind::UnexpectedEof,
std::io::ErrorKind::BrokenPipe,
std::io::ErrorKind::ConnectionReset,
std::io::ErrorKind::ConnectionAborted,
] {
let e: RpcError = std::io::Error::from(kind).into();
assert!(matches!(e, RpcError::PeerClosed), "{kind:?} -> {e:?}");
}
let other: RpcError = std::io::Error::from(std::io::ErrorKind::PermissionDenied).into();
assert!(matches!(other, RpcError::Io(_)));
}
#[test]
fn rpc_error_projects_onto_status_code() {
assert_eq!(
StatusCode::from(RpcError::PeerClosed),
StatusCode::DeadObject
);
assert_eq!(
StatusCode::from(RpcError::Truncated),
StatusCode::NotEnoughData
);
assert_eq!(
StatusCode::from(RpcError::FrameTooLarge {
declared: 1 << 30,
max: 1
}),
StatusCode::BadValue
);
assert_eq!(
StatusCode::from(RpcError::Protocol("bad")),
StatusCode::RpcError
);
}
#[test]
fn status_code_rpc_error_roundtrips() {
let v: i32 = StatusCode::RpcError.into();
assert_eq!(StatusCode::from(v), StatusCode::RpcError);
assert_eq!(format!("{}", StatusCode::RpcError), "RpcError");
assert_ne!(v, StatusCode::UnexpectedNull.into());
assert_ne!(v, StatusCode::FailedTransaction.into());
}
#[test]
fn rpc_parcel_hostile_array_len_is_bounded_not_oom() {
use crate::parcel::Parcel;
let mut p = Parcel::new();
p.set_for_rpc(true);
p.write(&i32::MAX).unwrap();
p.set_data_position(0);
assert!(
p.read::<Vec<i32>>().is_err(),
"hostile Vec<i32> len must error, not OOM"
);
let mut p = Parcel::new();
p.set_for_rpc(true);
p.write(&i32::MAX).unwrap();
p.write(&7i32).unwrap();
p.write(&8i32).unwrap();
p.set_data_position(0);
assert!(p.read::<Vec<i64>>().is_err());
for pat in [
vec![],
vec![0xFFu8; 4],
vec![0xFF, 0xFF, 0xFF, 0x7F, 0, 0, 0, 0],
(0..64u8).collect::<Vec<u8>>(),
] {
__fuzz_decode_rpc_parcel(&pat);
}
}
}