Import DebianArch xpc library

This commit is contained in:
Jackson Coxson
2025-01-24 16:50:48 -07:00
parent 81e3a598c6
commit 3e647bcb6d
11 changed files with 1342 additions and 0 deletions

32
Cargo.lock generated
View File

@@ -75,6 +75,17 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "async-recursion"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.4.0" version = "1.4.0"
@@ -426,7 +437,11 @@ dependencies = [
name = "idevice" name = "idevice"
version = "0.1.7" version = "0.1.7"
dependencies = [ dependencies = [
"async-recursion",
"base64",
"env_logger", "env_logger",
"indexmap",
"json",
"log", "log",
"openssl", "openssl",
"plist", "plist",
@@ -436,6 +451,7 @@ dependencies = [
"tokio", "tokio",
"tokio-openssl", "tokio-openssl",
"ureq", "ureq",
"uuid",
] ]
[[package]] [[package]]
@@ -467,6 +483,7 @@ checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
"serde",
] ]
[[package]] [[package]]
@@ -481,6 +498,12 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "json"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.169" version = "0.2.169"
@@ -1044,6 +1067,15 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"

View File

@@ -36,6 +36,11 @@ thiserror = { version = "2" }
log = { version = "0.4" } log = { version = "0.4" }
env_logger = { version = "0.11" } env_logger = { version = "0.11" }
openssl = { version = "0.10" } openssl = { version = "0.10" }
json = { version = "0.12" }
indexmap = { version = "2.7", features = ["serde"] }
uuid = { version = "1.12", features = ["serde"] }
async-recursion = { version = "1.1" }
base64 = { version = "0.22" }
# Binary dependencies # Binary dependencies
sha2 = { version = "0.10", optional = true } sha2 = { version = "0.10", optional = true }

62
src/http2/error.rs Normal file
View File

@@ -0,0 +1,62 @@
// DebianArch
use std::{array::TryFromSliceError, error::Error, io, num::TryFromIntError};
use tokio::sync::mpsc::error::SendError;
#[derive(Debug)]
pub enum Http2Error {
Io(io::Error),
SendError,
TryFromIntError(TryFromIntError),
TryFromSliceError(TryFromSliceError),
Custom(String),
}
impl From<io::Error> for Http2Error {
fn from(value: io::Error) -> Self {
Self::Io(value)
}
}
impl<T> From<SendError<T>> for Http2Error {
fn from(_: SendError<T>) -> Self {
Self::SendError
}
}
impl From<&str> for Http2Error {
fn from(value: &str) -> Self {
Self::Custom(value.to_string())
}
}
impl From<TryFromIntError> for Http2Error {
fn from(value: TryFromIntError) -> Self {
Self::TryFromIntError(value)
}
}
impl From<TryFromSliceError> for Http2Error {
fn from(value: TryFromSliceError) -> Self {
Self::TryFromSliceError(value)
}
}
impl std::fmt::Display for Http2Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Http2Error({})",
match self {
Self::Io(io) => io.to_string(),
Self::SendError => "SendError".to_string(),
Self::TryFromIntError(e) => e.to_string(),
Self::TryFromSliceError(e) => e.to_string(),
Self::Custom(s) => s.clone(),
}
)
}
}
impl Error for Http2Error {}

287
src/http2/h2.rs Normal file
View File

@@ -0,0 +1,287 @@
// DebianArch
use std::collections::HashMap;
use super::error::Http2Error;
pub const HTTP2_MAGIC: &[u8; 24] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
#[derive(Debug)]
pub struct Frame {
pub stream_id: u32,
pub flags: u8,
pub frame_type: FrameType,
pub body: Vec<u8>,
}
impl Frame {
pub fn new(stream_id: u32, flags: u8, frame_type: FrameType) -> Self {
Self {
stream_id,
flags,
frame_type,
body: Vec::new(),
}
}
pub fn set_body(&mut self, body: Vec<u8>) {
self.body = body;
}
pub fn deserialize(buf: &[u8]) -> Result<Self, Http2Error> {
let mut len_buf = buf[0..3].to_vec();
len_buf.insert(0, 0);
let body_len = u32::from_be_bytes(len_buf.try_into().unwrap()) as usize;
let frame_type = buf[3];
let flags = buf[4];
let stream_id = u32::from_be_bytes(buf[5..9].try_into()?);
let body = buf[9..9 + body_len].to_vec();
Ok(Self {
stream_id,
flags,
frame_type: frame_type.into(),
body,
})
}
}
impl Framable for Frame {
fn serialize(&self) -> Vec<u8> {
let mut res = Vec::new();
let body_len = (self.body.len() as u32).to_be_bytes();
res.extend_from_slice(&[body_len[1], body_len[2], body_len[3]]); // [0..3]
res.push(self.frame_type.into()); // [3]
res.push(self.flags); // flag mask [4]
res.extend_from_slice(&self.stream_id.to_be_bytes()); // [4..8]
res.extend_from_slice(&self.body); // [9..9+len]
res
}
}
pub trait Framable: From<Frame> {
fn serialize(&self) -> Vec<u8>;
}
// Frame implementations:
pub struct SettingsFrame {
frame: Frame,
pub settings: HashMap<u16, u32>,
}
impl SettingsFrame {
pub const HEADER_TABLE_SIZE: u16 = 0x01;
pub const ENABLE_PUSH: u16 = 0x02;
pub const MAX_CONCURRENT_STREAMS: u16 = 0x03;
pub const INITIAL_WINDOW_SIZE: u16 = 0x04;
pub const MAX_FRAME_SIZE: u16 = 0x05;
pub const MAX_HEADER_LIST_SIZE: u16 = 0x06;
pub const ENABLE_CONNECT_PROTOCOL: u16 = 0x08;
pub const ACK: u8 = 0x01;
pub fn new(/*stream_id: u32, */ settings: HashMap<u16, u32>, flags: u8) -> Self {
let mut body = Vec::new();
for setting in settings.clone() {
body.extend_from_slice(&setting.0.to_be_bytes());
body.extend_from_slice(&setting.1.to_be_bytes());
}
Self {
frame: Frame {
/*stream_id*/ stream_id: 0,
flags,
frame_type: FrameType::Settings,
body,
},
settings,
}
}
pub fn ack(/*stream_id: u32*/) -> Self {
Self {
frame: Frame {
/*stream_id*/ stream_id: 0,
flags: Self::ACK,
frame_type: FrameType::Settings,
body: Vec::new(),
},
settings: HashMap::new(),
}
}
}
impl Framable for SettingsFrame {
fn serialize(&self) -> Vec<u8> {
self.frame.serialize()
}
}
impl From<Frame> for SettingsFrame {
fn from(value: Frame) -> Self {
let mut idx = 0;
let mut settings = HashMap::new();
while idx < value.body.len() {
let key = u16::from_be_bytes(value.body[idx..idx + 2].try_into().unwrap());
let value = u32::from_be_bytes(value.body[idx + 2..idx + 2 + 4].try_into().unwrap());
settings.insert(key, value);
idx += 2 + 4;
}
Self {
frame: value,
settings,
}
}
}
pub struct WindowUpdateFrame {
frame: Frame,
pub window_increment: u32,
}
impl WindowUpdateFrame {
// the frame's stream identifier indicates the affected stream; in the latter, the value "0" indicates that the entire connection is the subject of the frame.
pub fn new(stream_id: u32, window_increment: u32) -> Self {
if window_increment == 0 {
panic!("PROTOCOL_ERROR");
}
Self {
frame: Frame {
stream_id,
flags: Default::default(),
frame_type: FrameType::WindowUpdate,
body: window_increment.to_be_bytes().to_vec(),
},
window_increment,
}
}
}
impl Framable for WindowUpdateFrame {
fn serialize(&self) -> Vec<u8> {
self.frame.serialize()
}
}
impl From<Frame> for WindowUpdateFrame {
fn from(value: Frame) -> Self {
let body = value.body.clone();
Self {
frame: value,
window_increment: u32::from_be_bytes(body.try_into().unwrap()),
}
}
}
pub struct HeadersFrame {
frame: Frame,
}
impl HeadersFrame {
pub const END_STREAM: u8 = 0x01;
pub const END_HEADERS: u8 = 0x04;
pub const PADDED: u8 = 0x08;
pub const PRIORITY: u8 = 0x20;
pub fn new(stream_id: u32, flags: u8) -> Self {
Self {
frame: Frame {
stream_id,
flags,
frame_type: FrameType::Headers,
body: Vec::new(),
},
}
}
}
impl Framable for HeadersFrame {
fn serialize(&self) -> Vec<u8> {
if self.frame.flags & Self::PADDED == Self::PADDED {
unimplemented!("haven't added padding support !")
};
if self.frame.flags & Self::PRIORITY == Self::PRIORITY {
unimplemented!("haven't added priority support !")
};
// let padding = 0; for 'PADDED' flag
// let priority_data = b""; // for PRIORITY flag
self.frame.serialize()
}
}
impl From<Frame> for HeadersFrame {
fn from(value: Frame) -> Self {
if value.flags & Self::PADDED == Self::PADDED {
unimplemented!("haven't added padding support !")
};
if value.flags & Self::PRIORITY == Self::PRIORITY {
unimplemented!("haven't added priority support !")
};
Self { frame: value }
}
}
pub struct DataFrame {
frame: Frame,
}
impl DataFrame {
// TODO: Handle padding flag.
pub fn new(stream_id: u32, data: Vec<u8>, flags: u8) -> Self {
Self {
frame: Frame {
stream_id,
flags,
frame_type: FrameType::Data,
body: data,
},
}
}
}
impl Framable for DataFrame {
fn serialize(&self) -> Vec<u8> {
self.frame.serialize()
}
}
impl From<Frame> for DataFrame {
fn from(value: Frame) -> Self {
Self { frame: value }
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub enum FrameType {
Data = 0,
Headers = 1,
Priority = 2,
RstStream = 3,
Settings = 4,
PushPromise = 5,
Ping = 6,
GoAway = 7,
WindowUpdate = 8,
Continuation = 9,
}
impl From<FrameType> for u8 {
fn from(value: FrameType) -> Self {
value as u8
}
}
impl From<u8> for FrameType {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute::<_, FrameType>(value) }
}
}
// impl Drop for Connection {
// fn drop(&mut self) {
// }
// }

218
src/http2/mod.rs Normal file
View File

@@ -0,0 +1,218 @@
// DebianArch
use async_recursion::async_recursion;
use error::Http2Error;
use std::collections::HashMap;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
sync::mpsc::{self, Receiver, Sender},
};
pub mod error;
pub mod h2;
pub mod padded;
use h2::{
DataFrame, Framable, Frame, FrameType, HeadersFrame, SettingsFrame, WindowUpdateFrame,
HTTP2_MAGIC,
};
pub type Channels = HashMap<u32, (Sender<Vec<u8>>, Receiver<Vec<u8>>)>;
pub struct Connection {
stream: crate::IdeviceSocket,
channels: Channels,
window_size: u32,
}
impl Connection {
pub const INIT_STREAM: u32 = 0;
pub const ROOT_CHANNEL: u32 = 1;
pub const REPLY_CHANNEL: u32 = 3;
pub async fn new(mut stream: crate::IdeviceSocket) -> Result<Self, Http2Error> {
stream.write_all(HTTP2_MAGIC).await?;
Ok(Self {
stream,
channels: HashMap::new(),
window_size: 1048576,
})
}
pub async fn send_frame<A: Framable>(&mut self, frame: A) -> Result<(), Http2Error> {
let body = &frame.serialize();
if body.len() > self.window_size as usize {
panic!("we need to chunk it :D")
}
self.stream.write_all(body).await?;
Ok(())
}
pub async fn read_data(&mut self) -> Result<Vec<u8>, Http2Error> {
loop {
let frame = self.read_frame().await?;
match frame.frame_type {
FrameType::Data => {
if frame.stream_id % 2 == 0 && !frame.body.is_empty() {
let frame_len: u32 = frame.body.len().try_into()?;
self.send_frame(WindowUpdateFrame::new(0, frame_len))
.await?;
self.send_frame(WindowUpdateFrame::new(frame.stream_id, frame_len))
.await?;
}
match self.channels.get_mut(&frame.stream_id) {
Some((sender, _receiver)) => {
sender.send(frame.body.clone()).await?;
}
None => {
let chan = mpsc::channel(100);
chan.0.send(frame.body.clone()).await?;
self.channels.insert(frame.stream_id, chan);
}
}
return Ok(frame.body);
}
FrameType::GoAway | FrameType::RstStream => {
let _last_streamid = u32::from_be_bytes(frame.body[0..4].try_into().unwrap());
return Err("connection closed, bye")?;
}
FrameType::Settings => {
let flags = frame.flags;
let settings_frame: SettingsFrame = frame.into();
if flags & SettingsFrame::ACK != SettingsFrame::ACK {
self.send_frame(SettingsFrame::ack()).await?;
}
if let Some(&window_size) = settings_frame
.settings
.get(&SettingsFrame::INITIAL_WINDOW_SIZE)
{
self.window_size = window_size;
}
}
_ => continue,
}
}
}
pub async fn read_frame(&mut self) -> Result<Frame, Http2Error> {
let mut length_buf = vec![0; 3];
self.stream.read_exact(&mut length_buf).await?;
length_buf.insert(0, 0);
let len = u32::from_be_bytes(length_buf.clone().try_into().unwrap()) as usize;
let mut rest = vec![0; 9 - 3 + len];
self.stream.read_exact(&mut rest).await?;
let mut content = vec![length_buf[1], length_buf[2], length_buf[3]];
content.extend_from_slice(&rest);
Frame::deserialize(&content)
}
// pub async fn multiplex_write(&mut self, stream_id: u32) -> Result<()> {}
// gets a Reader + Writer for a channel.
pub async fn write_streamid(
&mut self,
stream_id: u32,
data: Vec<u8>,
) -> Result<(), Http2Error> {
// TODO: If we ever allow concurrent writes we must not always send 'END_HEADERS'.
self.send_frame(HeadersFrame::new(stream_id, HeadersFrame::END_HEADERS))
.await?;
self.send_frame(DataFrame::new(stream_id, data, Default::default()))
.await?;
Ok(())
}
#[async_recursion]
pub async fn read_streamid(&mut self, stream_id: u32) -> Result<Vec<u8>, Http2Error> {
match self.channels.get_mut(&stream_id) {
Some((_sender, receiver)) => match receiver.try_recv().ok() {
Some(data) => Ok(data),
None => {
self.read_data().await?;
self.read_streamid(stream_id).await
}
},
None => {
self.read_data().await?;
self.read_streamid(stream_id).await
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn it_works() {
// let frame: Frame = Frame::deserialize(
// &BASE64_STANDARD
// .decode("AAAECAAAAAAAAA8AAQ==" /*"AAAABAEAAAAA"*/)
// .unwrap(),
// )
// .unwrap()
// .into();
// println!("supposed: {:x?}", frame.frame_type);
// return;
let mut client = Connection::new(Box::new(
tokio::net::TcpStream::connect("0.0.0.0:1010")
.await
.unwrap(),
))
.await
.unwrap();
// apart of spec, settings frame must be immediately sent after. Can be empty but must exist.
client
.send_frame(SettingsFrame::new(
[
(SettingsFrame::MAX_CONCURRENT_STREAMS, 100),
(SettingsFrame::INITIAL_WINDOW_SIZE, 1048576),
]
.into_iter()
.collect(),
Default::default(),
))
.await
.unwrap();
// apart of spec we are allowed to send frames before reading any from the server.
// 'INIT_STREAM'/0 applies to all stream_ids.
client
.send_frame(WindowUpdateFrame::new(Connection::INIT_STREAM, 983041))
.await
.unwrap();
// We create stream_id '1' by sending Header frame.
let mut frame = Frame::new(Connection::ROOT_CHANNEL, 5, FrameType::Headers);
frame.set_body(
[
0x41, 0x89, 0x2, 0xe0, 0x5c, 0xb, 0x82, 0xe0, 0x40, 0x10, 0x7f, 0x82, 0x84, 0x86,
0x50, 0x83, 0x9b, 0xd9, 0xab, 0x7a, 0x8d, 0xc4, 0x75, 0xa7, 0x4a, 0x6b, 0x58, 0x94,
0x18, 0xb5, 0x25, 0x81, 0x2e, 0xf,
]
.to_vec(),
);
// when server sends 'Settings' on a streamId that the client hasn't sent one on.
// then we must send them back one.
client
.send_frame(Frame::new(Connection::ROOT_CHANNEL, 1, FrameType::Settings))
.await
.unwrap();
client
.write_streamid(Connection::ROOT_CHANNEL, b"nibba\x00".to_vec())
.await
.unwrap();
// 'END_HEADERS' is sent before data.
println!(
"response: {:?}",
String::from_utf8_lossy(&client.read_streamid(1).await.unwrap())
);
}
}

12
src/http2/padded.rs Normal file
View File

@@ -0,0 +1,12 @@
// DebianArch
// use crate::h2::Frame;
// pub struct PaddedFrame {}
// impl PaddedFrame {
// pub fn new(body: &[u8]) -> Self {
// let pad_len = body[0];
// let stream_dependency = body[0..4];
// }
// }

View File

@@ -1,10 +1,12 @@
// Jackson Coxson // Jackson Coxson
pub mod heartbeat; pub mod heartbeat;
pub mod http2;
pub mod installation_proxy; pub mod installation_proxy;
pub mod lockdownd; pub mod lockdownd;
pub mod mounter; pub mod mounter;
pub mod pairing_file; pub mod pairing_file;
pub mod xpc;
use log::{debug, error}; use log::{debug, error};
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
@@ -15,6 +17,8 @@ use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
pub trait ReadWrite: AsyncRead + AsyncWrite + Unpin + Send + Sync + std::fmt::Debug {} pub trait ReadWrite: AsyncRead + AsyncWrite + Unpin + Send + Sync + std::fmt::Debug {}
impl<T: AsyncRead + AsyncWrite + Unpin + Send + Sync + std::fmt::Debug> ReadWrite for T {} impl<T: AsyncRead + AsyncWrite + Unpin + Send + Sync + std::fmt::Debug> ReadWrite for T {}
pub type IdeviceSocket = Box<dyn ReadWrite>;
pub struct Idevice { pub struct Idevice {
socket: Option<Box<dyn ReadWrite>>, // in a box for now to use the ReadWrite trait for further uses socket: Option<Box<dyn ReadWrite>>, // in a box for now to use the ReadWrite trait for further uses
label: String, label: String,

28
src/xpc/cdtunnel.rs Normal file
View File

@@ -0,0 +1,28 @@
// DebianArch
use json::JsonValue;
pub struct CDTunnel {}
impl CDTunnel {
const MAGIC: &'static [u8; 8] = b"CDTunnel";
pub fn decode(data: &[u8]) -> Result<JsonValue, Box<dyn std::error::Error>> {
let magic_len = CDTunnel::MAGIC.len();
if &data[0..magic_len] != CDTunnel::MAGIC {
Err("Invalid Magic")?;
}
let size = u16::from_be_bytes(data[magic_len..magic_len + 2].try_into()?) as usize;
let content = &data[magic_len + 2..magic_len + 2 + size];
Ok(json::parse(&String::from_utf8(content.to_vec())?)?)
}
pub fn encode(value: JsonValue) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut buf = Vec::new();
let json_str = value.dump();
buf.extend_from_slice(CDTunnel::MAGIC);
buf.extend_from_slice(&u16::to_be_bytes(json_str.len().try_into()?));
buf.extend_from_slice(json_str.as_bytes());
Ok(buf)
}
}

103
src/xpc/error.rs Normal file
View File

@@ -0,0 +1,103 @@
// DebianArch
use crate::http2::error::Http2Error;
use std::{
array::TryFromSliceError, error::Error, ffi::FromVecWithNulError, io, num::TryFromIntError,
str::Utf8Error,
};
#[derive(Debug)]
pub enum XPCError {
Io(io::Error),
Http2Error(Http2Error),
ParseError(ParseError),
Custom(String),
}
#[derive(Debug)]
pub enum ParseError {
TryFromSliceError(TryFromSliceError),
TryFromIntError(TryFromIntError),
FromVecWithNulError(FromVecWithNulError),
Utf8Error(Utf8Error),
}
impl From<TryFromSliceError> for XPCError {
fn from(value: TryFromSliceError) -> Self {
Self::ParseError(ParseError::TryFromSliceError(value))
}
}
impl From<TryFromIntError> for XPCError {
fn from(value: TryFromIntError) -> Self {
Self::ParseError(ParseError::TryFromIntError(value))
}
}
impl From<ParseError> for XPCError {
fn from(value: ParseError) -> Self {
Self::ParseError(value)
}
}
impl From<FromVecWithNulError> for XPCError {
fn from(value: FromVecWithNulError) -> Self {
Self::ParseError(ParseError::FromVecWithNulError(value))
}
}
impl From<Utf8Error> for XPCError {
fn from(value: Utf8Error) -> Self {
Self::ParseError(ParseError::Utf8Error(value))
}
}
impl From<io::Error> for XPCError {
fn from(value: io::Error) -> Self {
Self::Io(value)
}
}
impl From<&str> for XPCError {
fn from(value: &str) -> Self {
Self::Custom(value.to_string())
}
}
impl From<Http2Error> for XPCError {
fn from(value: Http2Error) -> Self {
Self::Http2Error(value)
}
}
impl std::fmt::Display for XPCError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"XPCError({})",
match self {
Self::Io(io) => io.to_string(),
Self::Http2Error(http2) => http2.to_string(),
Self::ParseError(e) => e.to_string(),
Self::Custom(s) => s.clone(),
}
)
}
}
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ParseError({})",
match self {
Self::TryFromSliceError(e) => e.to_string(),
Self::TryFromIntError(e) => e.to_string(),
Self::FromVecWithNulError(e) => e.to_string(),
Self::Utf8Error(e) => e.to_string(),
}
)
}
}
impl Error for XPCError {}

441
src/xpc/format.rs Normal file
View File

@@ -0,0 +1,441 @@
// DebianArch
use std::{
ffi::CString,
io::{BufRead, Cursor, Read},
ops::{BitOr, BitOrAssign},
};
use super::error::XPCError;
use indexmap::IndexMap;
use log::debug;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug)]
#[repr(u32)]
pub enum XPCFlag {
AlwaysSet,
DataFlag,
WantingReply,
InitHandshake,
Custom(u32),
}
impl From<XPCFlag> for u32 {
fn from(value: XPCFlag) -> Self {
match value {
XPCFlag::AlwaysSet => 0x00000001,
XPCFlag::DataFlag => 0x00000100,
XPCFlag::WantingReply => 0x00010000,
XPCFlag::InitHandshake => 0x00400000,
XPCFlag::Custom(inner) => inner,
}
}
}
impl BitOr for XPCFlag {
fn bitor(self, rhs: Self) -> Self::Output {
XPCFlag::Custom(u32::from(self) | u32::from(rhs))
}
type Output = XPCFlag;
}
impl BitOrAssign for XPCFlag {
fn bitor_assign(&mut self, rhs: Self) {
*self = self.bitor(rhs);
}
}
impl PartialEq for XPCFlag {
fn eq(&self, other: &Self) -> bool {
u32::from(*self) == u32::from(*other)
}
}
#[repr(u32)]
pub enum XPCType {
Bool = 0x00002000,
Dictionary = 0x0000f000,
Array = 0x0000e000,
Int64 = 0x00003000,
UInt64 = 0x00004000,
String = 0x00009000,
Data = 0x00008000,
Uuid = 0x0000a000,
}
impl TryFrom<u32> for XPCType {
type Error = XPCError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0x00002000 => Ok(Self::Bool),
0x0000f000 => Ok(Self::Dictionary),
0x0000e000 => Ok(Self::Array),
0x00003000 => Ok(Self::Int64),
0x00004000 => Ok(Self::UInt64),
0x00009000 => Ok(Self::String),
0x00008000 => Ok(Self::Data),
0x0000a000 => Ok(Self::Uuid),
_ => Err("Invalid XPCType")?,
}
}
}
pub type Dictionary = IndexMap<String, XPCObject>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum XPCObject {
Bool(bool),
Dictionary(Dictionary),
Array(Vec<XPCObject>),
Int64(i64),
UInt64(u64),
String(String),
Data(Vec<u8>),
Uuid(uuid::Uuid),
}
impl From<plist::Value> for XPCObject {
fn from(value: plist::Value) -> Self {
match value {
plist::Value::Array(v) => {
XPCObject::Array(v.iter().map(|item| XPCObject::from(item.clone())).collect())
}
plist::Value::Dictionary(v) => {
let mut dict = Dictionary::new();
for (k, v) in v.into_iter() {
dict.insert(k.clone(), XPCObject::from(v));
}
XPCObject::Dictionary(dict)
}
plist::Value::Boolean(v) => XPCObject::Bool(v),
plist::Value::Data(v) => XPCObject::Data(v),
plist::Value::Date(_) => todo!(),
plist::Value::Real(_) => todo!(),
plist::Value::Integer(v) => XPCObject::Int64(v.as_signed().unwrap()),
plist::Value::String(v) => XPCObject::String(v),
plist::Value::Uid(_) => todo!(),
_ => todo!(),
}
}
}
impl XPCObject {
pub fn to_plist(&self) -> plist::Value {
match self {
Self::Bool(v) => plist::Value::Boolean(*v),
Self::Uuid(uuid) => plist::Value::String(uuid.to_string()),
Self::UInt64(v) => plist::Value::Integer({ *v }.into()),
Self::Int64(v) => plist::Value::Integer({ *v }.into()),
Self::String(v) => plist::Value::String(v.clone()),
Self::Data(v) => plist::Value::Data(v.clone()),
Self::Array(v) => plist::Value::Array(v.iter().map(|item| item.to_plist()).collect()),
Self::Dictionary(v) => {
let mut dict = plist::Dictionary::new();
for (k, v) in v.into_iter() {
dict.insert(k.clone(), v.to_plist());
}
plist::Value::Dictionary(dict)
}
}
}
pub fn to_value<T: Serialize>(value: &T) -> Self {
match plist::to_value(value) {
Ok(v) => Self::from(v),
Err(_) => panic!("oof"),
}
}
pub fn encode(&self) -> Result<Vec<u8>, XPCError> {
let mut buf = Vec::new();
buf.extend_from_slice(&0x42133742_u32.to_le_bytes());
buf.extend_from_slice(&0x00000005_u32.to_le_bytes());
self.encode_object(&mut buf)?;
Ok(buf)
}
fn encode_object(&self, buf: &mut Vec<u8>) -> Result<(), XPCError> {
match self {
XPCObject::Bool(val) => {
buf.extend_from_slice(&(XPCType::Bool as u32).to_le_bytes());
buf.push(if *val { 0 } else { 1 });
buf.extend_from_slice(&[0].repeat(3));
}
XPCObject::Dictionary(dict) => {
buf.extend_from_slice(&(XPCType::Dictionary as u32).to_le_bytes());
buf.extend_from_slice(&0_u32.to_le_bytes()); // represents l, no idea what this is.
buf.extend_from_slice(&(dict.len() as u32).to_le_bytes());
for (k, v) in dict {
let padding = Self::calculate_padding(k.len() + 1);
buf.extend_from_slice(k.as_bytes());
buf.push(0);
buf.extend_from_slice(&[0].repeat(padding));
v.encode_object(buf)?;
}
}
XPCObject::Array(items) => {
buf.extend_from_slice(&(XPCType::Array as u32).to_le_bytes());
buf.extend_from_slice(&0_u32.to_le_bytes()); // represents l, no idea what this is.
buf.extend_from_slice(&(items.len() as u32).to_le_bytes());
for item in items {
item.encode_object(buf)?;
}
}
XPCObject::Int64(num) => {
buf.extend_from_slice(&(XPCType::Int64 as u32).to_le_bytes());
buf.extend_from_slice(&num.to_le_bytes());
}
XPCObject::UInt64(num) => {
buf.extend_from_slice(&(XPCType::UInt64 as u32).to_le_bytes());
buf.extend_from_slice(&num.to_le_bytes());
}
XPCObject::String(item) => {
let l = item.len() + 1;
let padding = Self::calculate_padding(l);
buf.extend_from_slice(&(XPCType::String as u32).to_le_bytes());
buf.extend_from_slice(&(l as u32).to_le_bytes());
buf.extend_from_slice(item.as_bytes());
buf.push(0);
buf.extend_from_slice(&[0].repeat(padding));
}
XPCObject::Data(data) => {
let l = data.len();
let padding = Self::calculate_padding(l);
buf.extend_from_slice(&(XPCType::Data as u32).to_le_bytes());
buf.extend_from_slice(&(l as u32).to_le_bytes());
buf.extend_from_slice(data);
buf.extend_from_slice(&[0].repeat(padding));
}
XPCObject::Uuid(uuid) => {
buf.extend_from_slice(&(XPCType::Uuid as u32).to_le_bytes());
buf.extend_from_slice(&16_u32.to_le_bytes());
buf.extend_from_slice(uuid.as_bytes());
}
}
Ok(())
}
pub fn decode(buf: &[u8]) -> Result<Self, XPCError> {
let magic = u32::from_le_bytes(buf[0..4].try_into()?);
if magic != 0x42133742 {
Err("Invalid magic for XPCObject")?
}
let version = u32::from_le_bytes(buf[4..8].try_into()?);
if version != 0x00000005 {
Err("Unexpected version for XPCObject")?
}
Self::decode_object(&mut Cursor::new(&buf[8..]))
}
fn decode_object(mut cursor: &mut Cursor<&[u8]>) -> Result<Self, XPCError> {
let mut buf_32: [u8; 4] = Default::default();
cursor.read_exact(&mut buf_32)?;
let xpc_type = u32::from_le_bytes(buf_32);
let xpc_type: XPCType = xpc_type.try_into()?;
match xpc_type {
XPCType::Dictionary => {
let mut ret = IndexMap::new();
cursor.read_exact(&mut buf_32)?;
let _l = u32::from_le_bytes(buf_32);
cursor.read_exact(&mut buf_32)?;
let num_entries = u32::from_le_bytes(buf_32);
for _i in 0..num_entries {
let mut key_buf = Vec::new();
BufRead::read_until(&mut cursor, 0, &mut key_buf)?;
let key = CString::from_vec_with_nul(key_buf)?.to_str()?.to_string();
let padding = Self::calculate_padding(key.len() + 1);
BufRead::consume(&mut cursor, padding);
ret.insert(key, Self::decode_object(cursor)?);
}
Ok(XPCObject::Dictionary(ret))
}
XPCType::Array => {
cursor.read_exact(&mut buf_32)?;
let _l = u32::from_le_bytes(buf_32);
cursor.read_exact(&mut buf_32)?;
let num_entries = u32::from_le_bytes(buf_32);
let mut ret = Vec::new();
for _i in 0..num_entries {
ret.push(Self::decode_object(cursor)?);
}
Ok(XPCObject::Array(ret))
}
XPCType::Int64 => {
let mut buf: [u8; 8] = Default::default();
cursor.read_exact(&mut buf)?;
Ok(XPCObject::Int64(i64::from_le_bytes(buf)))
}
XPCType::UInt64 => {
let mut buf: [u8; 8] = Default::default();
cursor.read_exact(&mut buf)?;
Ok(XPCObject::UInt64(u64::from_le_bytes(buf)))
}
XPCType::String => {
// 'l' includes utf8 '\0' character.
cursor.read_exact(&mut buf_32)?;
let l = u32::from_le_bytes(buf_32) as usize;
let padding = Self::calculate_padding(l);
let mut key_buf = vec![0; l];
cursor.read_exact(&mut key_buf)?;
let key = CString::from_vec_with_nul(key_buf)?.to_str()?.to_string();
BufRead::consume(&mut cursor, padding);
Ok(XPCObject::String(key))
}
XPCType::Bool => {
let mut buf: [u8; 4] = Default::default();
cursor.read_exact(&mut buf)?;
Ok(XPCObject::Bool(buf[0] != 0))
}
XPCType::Data => {
cursor.read_exact(&mut buf_32)?;
let l = u32::from_le_bytes(buf_32) as usize;
let padding = Self::calculate_padding(l);
let mut data = vec![0; l];
cursor.read_exact(&mut data)?;
BufRead::consume(&mut cursor, padding);
Ok(XPCObject::Data(data))
}
XPCType::Uuid => {
let mut data: [u8; 16] = Default::default();
cursor.read_exact(&mut data)?;
Ok(XPCObject::Uuid(uuid::Builder::from_bytes(data).into_uuid()))
}
}
}
pub fn as_dictionary(&self) -> Option<&Dictionary> {
match self {
XPCObject::Dictionary(dict) => Some(dict),
_ => None,
}
}
pub fn as_string(&self) -> Option<&str> {
match self {
XPCObject::String(s) => Some(s),
_ => None,
}
}
pub fn as_bool(&self) -> Option<&bool> {
match self {
XPCObject::Bool(b) => Some(b),
_ => None,
}
}
pub fn as_signed_integer(&self) -> Option<i64> {
match self {
XPCObject::String(s) => s.parse().ok(),
XPCObject::Int64(v) => Some(*v),
_ => None,
}
}
pub fn as_unsigned_integer(&self) -> Option<u64> {
match self {
XPCObject::String(s) => s.parse().ok(),
XPCObject::UInt64(v) => Some(*v),
_ => None,
}
}
fn calculate_padding(len: usize) -> usize {
let c = ((len as f64) / 4.0).ceil();
(c * 4.0 - (len as f64)) as usize
}
}
impl From<Dictionary> for XPCObject {
fn from(value: Dictionary) -> Self {
XPCObject::Dictionary(value)
}
}
#[derive(Debug)]
pub struct XPCMessage {
pub flags: u32,
pub message: Option<XPCObject>,
pub message_id: Option<u64>,
}
impl XPCMessage {
pub fn new(
flags: Option<XPCFlag>,
message: Option<XPCObject>,
message_id: Option<u64>,
) -> XPCMessage {
XPCMessage {
flags: flags.unwrap_or(XPCFlag::AlwaysSet).into(),
message,
message_id,
}
}
pub fn decode(data: &[u8]) -> Result<XPCMessage, XPCError> {
if data.len() < 24 {
Err("XPCMessage must be at least 24 bytes.")?
}
let magic = u32::from_le_bytes(data[0..4].try_into()?);
if magic != 0x29b00b92_u32 {
Err("XPCMessage magic is invalid.")?
}
let flags = u32::from_le_bytes(data[4..8].try_into()?);
let body_len = u64::from_le_bytes(data[8..16].try_into()?);
let message_id = u64::from_le_bytes(data[16..24].try_into()?);
if body_len + 24 > data.len().try_into()? {
Err("XPCMessage body length given is incorrect.")?
}
// for some reason the above if check doesn't work ???
debug!("Body length {} : {}", body_len, data.len());
if body_len == 0 {
return Ok(XPCMessage {
flags,
message: None,
message_id: Some(message_id),
});
}
Ok(XPCMessage {
flags,
message: Some(XPCObject::decode(&data[24..24 + body_len as usize])?),
message_id: Some(message_id),
})
}
pub fn encode(self, message_id: u64) -> Result<Vec<u8>, XPCError> {
let mut out = 0x29b00b92_u32.to_le_bytes().to_vec();
out.extend_from_slice(&self.flags.to_le_bytes());
match self.message {
Some(message) => {
let body = message.encode()?;
out.extend_from_slice(&(body.len() as u64).to_le_bytes()); // body length
out.extend_from_slice(&message_id.to_le_bytes()); // messageId
out.extend_from_slice(&body);
}
_ => {
out.extend_from_slice(&0_u64.to_le_bytes());
out.extend_from_slice(&message_id.to_le_bytes());
}
}
Ok(out)
}
}

150
src/xpc/mod.rs Normal file
View File

@@ -0,0 +1,150 @@
// Thanks DebianArch
use crate::http2::{
self,
h2::{SettingsFrame, WindowUpdateFrame},
};
use error::XPCError;
use format::{XPCFlag, XPCMessage, XPCObject};
use log::debug;
use tokio::net::{TcpStream, ToSocketAddrs};
pub mod cdtunnel;
pub mod error;
pub mod format;
pub struct XPCConnection {
inner: http2::Connection,
}
impl XPCConnection {
pub const ROOT_CHANNEL: u32 = http2::Connection::ROOT_CHANNEL;
pub const REPLY_CHANNEL: u32 = http2::Connection::REPLY_CHANNEL;
const INIT_STREAM: u32 = http2::Connection::INIT_STREAM;
pub async fn connect<A: ToSocketAddrs>(addr: A) -> Result<Self, XPCError> {
Self::new(Box::new(TcpStream::connect(addr).await?)).await
}
pub async fn new(stream: crate::IdeviceSocket) -> Result<Self, XPCError> {
let mut client = http2::Connection::new(stream).await?;
client
.send_frame(SettingsFrame::new(
[
(SettingsFrame::MAX_CONCURRENT_STREAMS, 100),
(SettingsFrame::INITIAL_WINDOW_SIZE, 1048576),
]
.into_iter()
.collect(),
Default::default(),
))
.await?;
client
.send_frame(WindowUpdateFrame::new(Self::INIT_STREAM, 983041))
.await?;
let mut xpc_client = Self { inner: client };
xpc_client
.send_recv_message(
Self::ROOT_CHANNEL,
XPCMessage::new(
Some(XPCFlag::AlwaysSet),
Some(XPCObject::Dictionary(Default::default())),
None,
),
)
.await?;
// we are here. we send data to stream_id 3 yet we get data from stream 1 ???
xpc_client
.send_recv_message(
Self::REPLY_CHANNEL,
XPCMessage::new(
Some(XPCFlag::InitHandshake | XPCFlag::AlwaysSet),
None,
None,
),
)
.await?;
xpc_client
.send_recv_message(
Self::ROOT_CHANNEL,
XPCMessage::new(Some(XPCFlag::Custom(0x201)), None, None),
)
.await?;
Ok(xpc_client)
}
pub async fn send_recv_message(
&mut self,
stream_id: u32,
message: XPCMessage,
) -> Result<XPCMessage, XPCError> {
self.send_message(stream_id, message).await?;
self.read_message(stream_id).await
}
pub async fn send_message(
&mut self,
stream_id: u32,
message: XPCMessage,
) -> Result<(), XPCError> {
self.inner
.write_streamid(stream_id, message.encode(0)?)
.await
.map_err(|err| err.into())
}
pub async fn read_message(&mut self, stream_id: u32) -> Result<XPCMessage, XPCError> {
let mut buf = self.inner.read_streamid(stream_id).await?;
loop {
match XPCMessage::decode(&buf) {
Ok(decoded) => {
debug!("Decoded message: {:?}", decoded);
return Ok(decoded);
}
Err(err) => {
log::error!("Error decoding message: {:?}", err);
buf.extend_from_slice(&self.inner.read_streamid(stream_id).await?);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn it_works() {
// assert_eq!(
// XPCFlag::InitHandshake | XPCFlag::AlwaysSet,
// XPCFlag::Custom(0x00400000 | 0x00000001)
// );
// let mut buf = Vec::new();
// let plst = XPCMessage::decode(&buf)
// .unwrap()
// .message
// .unwrap()
// .to_plist();
// plst.to_file_xml("rayan.bin").unwrap();
// return;
let mut client = XPCConnection::new(Box::new(
TcpStream::connect(("fd35:d15d:9272::1", 64634))
.await
.unwrap(),
))
.await
.unwrap();
let data = client
.read_message(http2::Connection::ROOT_CHANNEL)
.await
.unwrap();
println!("ayo: {:?}", data);
}
}