mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 06:26:15 +01:00
Import DebianArch xpc library
This commit is contained in:
32
Cargo.lock
generated
32
Cargo.lock
generated
@@ -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"
|
||||||
|
|||||||
@@ -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
62
src/http2/error.rs
Normal 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
287
src/http2/h2.rs
Normal 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
218
src/http2/mod.rs
Normal 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
12
src/http2/padded.rs
Normal 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];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -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
28
src/xpc/cdtunnel.rs
Normal 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
103
src/xpc/error.rs
Normal 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
441
src/xpc/format.rs
Normal 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
150
src/xpc/mod.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user