1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// jkcoxson
use std::{ffi::CString, future::Future, pin::Pin};
use crate::{bindings as unsafe_bindings, error::HeartbeatError, idevice::Device};
use log::info;
use plist_plus::Plist;
/// A required service for most other services.
/// iOS will close other connections if there is no active heartbeat client
///
/// # Protocol
/// The hearbeat protocol goes as follows
/// * The host requests a connection through lockdown
/// * The iOS connection accepts the connection
/// * The host will wait for a hearbeat packet from the iDevice
/// * The host will echo back the message at the interval defined in the message (give buffer time)
///
/// **Note** The device will kill the heartbeat connection if packets are echoed too frequently
pub struct HeartbeatClient {
pub(crate) pointer: unsafe_bindings::heartbeat_client_t,
}
unsafe impl Send for HeartbeatClient {}
unsafe impl Sync for HeartbeatClient {}
unsafe impl Send for HeartbeatClientFuture {}
unsafe impl Sync for HeartbeatClientFuture {}
impl HeartbeatClient {
/// Starts a new service with heartbeat
/// # Arguments
/// * `device` - The device to create the sevice with
/// * `label` - The label to give the connection
/// # Returns
/// A struct containing the handle to the service
///
/// ***Verified:*** False
pub fn new(device: &Device, label: impl Into<String>) -> Result<Self, HeartbeatError> {
let mut pointer = unsafe { std::mem::zeroed() };
let label_c_str = CString::new(label.into()).unwrap();
let label_ptr = label_c_str.as_ptr();
let result = unsafe {
unsafe_bindings::heartbeat_client_start_service(device.pointer, &mut pointer, label_ptr)
}
.into();
if result != HeartbeatError::Success {
return Err(result);
}
Ok(Self {
pointer,
// phantom: std::marker::PhantomData,
})
}
/// Send data to the hearbeat service
/// # Arguments
/// * `message` - A plist containing the message
/// # Returns
/// *none*
///
/// ***Verified:*** False
pub fn send(&self, message: Plist) -> Result<(), HeartbeatError> {
let result =
unsafe { unsafe_bindings::heartbeat_send(self.pointer, message.get_pointer()) }.into();
if result != HeartbeatError::Success {
return Err(result);
}
Ok(())
}
/// Receive data from the heartbeat service.
/// If the error is a MuxError, this usually means that the device has disconnected.
/// # Arguments
/// * `timeout` - How long to wait for a message. If 0, this will block indefinitely.
/// # Returns
/// The message as a plist
///
/// ***Verified:*** False
pub fn receive(&self, timeout: u32) -> Result<Plist, HeartbeatError> {
let mut plist_ptr = unsafe { std::mem::zeroed() };
if timeout == 0 {
let result =
unsafe { unsafe_bindings::heartbeat_receive(self.pointer, &mut plist_ptr) }.into();
if result != HeartbeatError::Success {
return Err(result);
}
} else {
let result = unsafe {
unsafe_bindings::heartbeat_receive_with_timeout(
self.pointer,
&mut plist_ptr,
timeout,
)
}
.into();
if result != HeartbeatError::Success {
return Err(result);
}
}
Ok(plist_ptr.into())
}
/// Receive data from the heartbeat service as a future.
/// If the error is a MuxError, this usually means that the device has disconnected.
/// # Arguments
/// * `timeout` - How long to wait for a message. If 0, this will block indefinitely.
/// # Returns
/// The message as a plist
///
/// ***Verified:*** False
pub fn receive_async(&self, timeout: u32) -> HeartbeatClientFuture {
HeartbeatClientFuture {
pointer: self.pointer,
start_time: std::time::Instant::now(),
timeout,
}
}
}
pub struct HeartbeatClientFuture {
pointer: unsafe_bindings::heartbeat_client_t,
start_time: std::time::Instant,
timeout: u32,
}
impl HeartbeatClientFuture {
fn receive(&self) -> Result<Plist, HeartbeatError> {
let mut plist_ptr = unsafe { std::mem::zeroed() };
let result = unsafe {
unsafe_bindings::heartbeat_receive_with_timeout(self.pointer, &mut plist_ptr, 1000)
}
.into();
if result != HeartbeatError::Success {
return Err(result);
}
Ok(plist_ptr.into())
}
}
impl Future for HeartbeatClientFuture {
type Output = Result<Plist, HeartbeatError>;
fn poll(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
// Determine if we have passed the timeout + 5 seconds
let now = std::time::Instant::now();
let elapsed = now.duration_since(self.start_time);
let timeout = self.timeout as u128;
let elapsed_ms = elapsed.as_millis();
if elapsed_ms > timeout + 5000 {
// Query the heartbeat service for a message
match self.receive() {
Ok(plist) => {
info!("Received heartbeat message");
return std::task::Poll::Ready(Ok(plist));
}
Err(error) => {
info!("Received heartbeat error: {:?}", error);
return std::task::Poll::Ready(Err(error));
}
}
}
cx.waker().wake_by_ref();
std::task::Poll::Pending
}
}
impl Drop for HeartbeatClient {
fn drop(&mut self) {
info!("Dropping heartbeat client");
unsafe {
unsafe_bindings::heartbeat_client_free(self.pointer);
}
}
}