mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 14:36:16 +01:00
Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a523f0cb9c | ||
|
|
cb375f88a1 | ||
|
|
c5aa731ee5 | ||
|
|
38a3a558b5 | ||
|
|
496e099187 | ||
|
|
77ea34f820 | ||
|
|
9a71279fe9 | ||
|
|
142708c289 | ||
|
|
5bb9330cf6 | ||
|
|
ead5fbf3d3 | ||
|
|
f44a5c0779 | ||
|
|
1db78e6a8d | ||
|
|
bb64dc0b1c | ||
|
|
a4e17ea076 | ||
|
|
6dcfd4bc4c | ||
|
|
602e1ba855 | ||
|
|
ae39fcb7df | ||
|
|
13be1ae377 | ||
|
|
96b380ebc9 | ||
|
|
189dd5caf2 | ||
|
|
2eebbff172 | ||
|
|
166c497878 | ||
|
|
6d9f0987c1 | ||
|
|
081cb2f8d8 | ||
|
|
35c3d61355 | ||
|
|
328224d46c | ||
|
|
d8bff83753 | ||
|
|
ae5071a309 | ||
|
|
83e43aa3d6 | ||
|
|
2a6631f3da | ||
|
|
e3c12ddf98 | ||
|
|
44e8b83698 | ||
|
|
9776516544 | ||
|
|
080fea02eb | ||
|
|
39d454d77d | ||
|
|
a3dcac93b2 | ||
|
|
d2375e8f5c | ||
|
|
9e8abb7d37 | ||
|
|
c60f0d102b | ||
|
|
5f1e03911f | ||
|
|
c1b7009a7b | ||
|
|
a708db6307 | ||
|
|
c432627659 | ||
|
|
c0ec9b4bcd | ||
|
|
c1bc887fd4 | ||
|
|
761cb06418 | ||
|
|
db4547e0da | ||
|
|
59c3c3a12c | ||
|
|
f11a1bafff | ||
|
|
c9ca113239 | ||
|
|
08d6b41536 | ||
|
|
db894120da | ||
|
|
13c5b48b1c | ||
|
|
e31f39eac0 | ||
|
|
fbdc290d88 | ||
|
|
247acb192d | ||
|
|
d15c255524 | ||
|
|
c8e5f52ccd | ||
|
|
6d4bd7e853 | ||
|
|
4fa46e4c54 | ||
|
|
b26dd17b13 | ||
|
|
105a9b2837 | ||
|
|
7da735f141 | ||
|
|
7527cdff7b | ||
|
|
ec4663e93d | ||
|
|
20f00e38dd | ||
|
|
a297eed156 | ||
|
|
18b8b7295c | ||
|
|
0ccec70ed8 | ||
|
|
7805f943af | ||
|
|
5ed2144d9e | ||
|
|
dd2db92967 | ||
|
|
94624f07af | ||
|
|
a7daac3a46 | ||
|
|
779db7ecec | ||
|
|
c10f4da9f1 | ||
|
|
ea61459df2 | ||
|
|
cf7c92f8ad | ||
|
|
2ffeb0f25c | ||
|
|
aff3ef589f | ||
|
|
11c8f98b4f | ||
|
|
ef14b7669d | ||
|
|
0490c246be | ||
|
|
9a656a2a0e | ||
|
|
87ad894875 | ||
|
|
7853f2d6d0 | ||
|
|
526773d8ca | ||
|
|
eb37facd04 | ||
|
|
b897c3a126 | ||
|
|
fb3043b3e0 | ||
|
|
b0f871af99 | ||
|
|
7507b9609c | ||
|
|
4710ffce34 |
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -30,8 +30,12 @@ jobs:
|
|||||||
|
|
||||||
- name: Install rustup targets
|
- name: Install rustup targets
|
||||||
run: |
|
run: |
|
||||||
|
rustup target add aarch64-apple-ios && \
|
||||||
|
rustup target add x86_64-apple-ios && \
|
||||||
|
rustup target add aarch64-apple-ios-sim && \
|
||||||
rustup target add aarch64-apple-darwin && \
|
rustup target add aarch64-apple-darwin && \
|
||||||
rustup target add x86_64-apple-darwin && cargo install --force --locked bindgen-cli
|
rustup target add x86_64-apple-darwin && \
|
||||||
|
cargo install --force --locked bindgen-cli
|
||||||
|
|
||||||
- name: Build all Apple targets and examples/tools
|
- name: Build all Apple targets and examples/tools
|
||||||
run: |
|
run: |
|
||||||
@@ -44,6 +48,12 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
target/*apple*/release/libidevice_ffi.a
|
target/*apple*/release/libidevice_ffi.a
|
||||||
|
|
||||||
|
- name: Upload macOS+iOS XCFramework
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: idevice-xcframework
|
||||||
|
path: swift/bundle.zip
|
||||||
|
|
||||||
- name: Upload C examples/tools
|
- name: Upload C examples/tools
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
1557
Cargo.lock
generated
1557
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -32,8 +32,8 @@ endif()
|
|||||||
# ---- Build the C++ wrapper library -----------------------------------------
|
# ---- Build the C++ wrapper library -----------------------------------------
|
||||||
|
|
||||||
# Collect your .cpps
|
# Collect your .cpps
|
||||||
file(GLOB IDEVICE_CPP_SOURCES
|
file(GLOB_RECURSE IDEVICE_CPP_SOURCES
|
||||||
${IDEVICE_CPP_SRC_DIR}/*.cpp
|
"${IDEVICE_CPP_SRC_DIR}/*.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
file(GLOB PLIST_CPP_SOURCES
|
file(GLOB PLIST_CPP_SOURCES
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include <idevice++/core_device_proxy.hpp>
|
#include <idevice++/core_device_proxy.hpp>
|
||||||
|
#include <idevice++/dvt/location_simulation.hpp>
|
||||||
|
#include <idevice++/dvt/remote_server.hpp>
|
||||||
#include <idevice++/ffi.hpp>
|
#include <idevice++/ffi.hpp>
|
||||||
#include <idevice++/location_simulation.hpp>
|
|
||||||
#include <idevice++/provider.hpp>
|
#include <idevice++/provider.hpp>
|
||||||
#include <idevice++/readwrite.hpp>
|
#include <idevice++/readwrite.hpp>
|
||||||
#include <idevice++/remote_server.hpp>
|
|
||||||
#include <idevice++/rsd.hpp>
|
#include <idevice++/rsd.hpp>
|
||||||
#include <idevice++/usbmuxd.hpp>
|
#include <idevice++/usbmuxd.hpp>
|
||||||
|
|
||||||
|
|||||||
108
cpp/examples/screenshot.cpp
Normal file
108
cpp/examples/screenshot.cpp
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <idevice++/bindings.hpp>
|
||||||
|
#include <idevice++/core_device_proxy.hpp>
|
||||||
|
#include <idevice++/dvt/remote_server.hpp>
|
||||||
|
#include <idevice++/dvt/screenshot.hpp>
|
||||||
|
#include <idevice++/ffi.hpp>
|
||||||
|
#include <idevice++/provider.hpp>
|
||||||
|
#include <idevice++/readwrite.hpp>
|
||||||
|
#include <idevice++/rsd.hpp>
|
||||||
|
#include <idevice++/usbmuxd.hpp>
|
||||||
|
|
||||||
|
using namespace IdeviceFFI;
|
||||||
|
|
||||||
|
[[noreturn]]
|
||||||
|
static void die(const char* msg, const FfiError& e) {
|
||||||
|
std::cerr << msg << ": " << e.message << "\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
idevice_init_logger(Debug, Disabled, NULL);
|
||||||
|
// Usage:
|
||||||
|
// take_screenshot <output.png>
|
||||||
|
if (argc != 2) {
|
||||||
|
std::cerr << "Usage:\n"
|
||||||
|
<< " " << argv[0] << " <output.png>\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string out_path = argv[1];
|
||||||
|
|
||||||
|
// 1) Connect to usbmuxd and pick first device
|
||||||
|
auto mux = UsbmuxdConnection::default_new(/*tag*/ 0).expect("failed to connect to usbmuxd");
|
||||||
|
|
||||||
|
auto devices = mux.get_devices().expect("failed to list devices");
|
||||||
|
if (devices.empty()) {
|
||||||
|
std::cerr << "no devices connected\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
auto& dev = (devices)[0];
|
||||||
|
|
||||||
|
auto udid = dev.get_udid();
|
||||||
|
if (udid.is_none()) {
|
||||||
|
std::cerr << "device has no UDID\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
auto mux_id = dev.get_id();
|
||||||
|
if (mux_id.is_none()) {
|
||||||
|
std::cerr << "device has no mux id\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Provider via default usbmuxd addr
|
||||||
|
auto addr = UsbmuxdAddr::default_new();
|
||||||
|
const uint32_t tag = 0;
|
||||||
|
const std::string label = "screenshot-client";
|
||||||
|
|
||||||
|
auto provider =
|
||||||
|
Provider::usbmuxd_new(std::move(addr), tag, udid.unwrap(), mux_id.unwrap(), label)
|
||||||
|
.expect("failed to create provider");
|
||||||
|
|
||||||
|
// 3) CoreDeviceProxy
|
||||||
|
auto cdp = CoreDeviceProxy::connect(provider).unwrap_or_else(
|
||||||
|
[](FfiError e) -> CoreDeviceProxy { die("failed to connect CoreDeviceProxy", e); });
|
||||||
|
|
||||||
|
auto rsd_port = cdp.get_server_rsd_port().unwrap_or_else(
|
||||||
|
[](FfiError err) -> uint16_t { die("failed to get server RSD port", err); });
|
||||||
|
|
||||||
|
// 4) Create software tunnel adapter (consumes proxy)
|
||||||
|
auto adapter =
|
||||||
|
std::move(cdp).create_tcp_adapter().expect("failed to create software tunnel adapter");
|
||||||
|
|
||||||
|
// 5) Connect adapter to RSD → ReadWrite stream
|
||||||
|
auto stream = adapter.connect(rsd_port).expect("failed to connect RSD stream");
|
||||||
|
|
||||||
|
// 6) RSD handshake (consumes stream)
|
||||||
|
auto rsd = RsdHandshake::from_socket(std::move(stream)).expect("failed RSD handshake");
|
||||||
|
|
||||||
|
// 7) RemoteServer over RSD (borrows adapter + handshake)
|
||||||
|
auto rs = RemoteServer::connect_rsd(adapter, rsd).expect("failed to connect to RemoteServer");
|
||||||
|
|
||||||
|
// 8) ScreenshotClient (borrows RemoteServer)
|
||||||
|
auto ss = ScreenshotClient::create(rs).unwrap_or_else(
|
||||||
|
[](FfiError e) -> ScreenshotClient { die("failed to create ScreenshotClient", e); });
|
||||||
|
|
||||||
|
// 9) Capture screenshot
|
||||||
|
auto buf = ss.take_screenshot().unwrap_or_else(
|
||||||
|
[](FfiError e) -> std::vector<uint8_t> { die("failed to capture screenshot", e); });
|
||||||
|
|
||||||
|
// 10) Write PNG file
|
||||||
|
std::ofstream out(out_path, std::ios::binary);
|
||||||
|
if (!out.is_open()) {
|
||||||
|
std::cerr << "failed to open output file: " << out_path << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
std::cout << "Screenshot saved to " << out_path << " (" << buf.size() << " bytes)\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -7,81 +7,101 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
1914C7972E623CC2002EAB6E /* option.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 1914C7962E623CC2002EAB6E /* option.hpp */; };
|
196C57452EA7F5DC00188F4B /* location_simulation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56B42EA7F53000188F4B /* location_simulation.cpp */; };
|
||||||
1914C7992E623CC8002EAB6E /* result.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 1914C7982E623CC8002EAB6E /* result.hpp */; };
|
196C57462EA7F5DC00188F4B /* process_control.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56B52EA7F53000188F4B /* process_control.cpp */; };
|
||||||
198077932E5CA6EF00CB501E /* adapter_stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1980776C2E5CA69800CB501E /* adapter_stream.cpp */; };
|
196C57472EA7F5DC00188F4B /* remote_server.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56B62EA7F53000188F4B /* remote_server.cpp */; };
|
||||||
198077942E5CA6EF00CB501E /* app_service.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1980776D2E5CA69800CB501E /* app_service.cpp */; };
|
196C57482EA7F5DC00188F4B /* screenshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56B72EA7F53000188F4B /* screenshot.cpp */; };
|
||||||
198077952E5CA6EF00CB501E /* core_device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1980776E2E5CA69800CB501E /* core_device.cpp */; };
|
196C57492EA7F5DC00188F4B /* adapter_stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56B92EA7F53000188F4B /* adapter_stream.cpp */; };
|
||||||
198077962E5CA6EF00CB501E /* debug_proxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1980776F2E5CA69800CB501E /* debug_proxy.cpp */; };
|
196C574A2EA7F5DC00188F4B /* app_service.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56BA2EA7F53000188F4B /* app_service.cpp */; };
|
||||||
198077972E5CA6EF00CB501E /* diagnosticsservice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 198077702E5CA69800CB501E /* diagnosticsservice.cpp */; };
|
196C574B2EA7F5DC00188F4B /* core_device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56BB2EA7F53000188F4B /* core_device.cpp */; };
|
||||||
198077982E5CA6EF00CB501E /* ffi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 198077712E5CA69800CB501E /* ffi.cpp */; };
|
196C574C2EA7F5DC00188F4B /* debug_proxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56BC2EA7F53000188F4B /* debug_proxy.cpp */; };
|
||||||
198077992E5CA6EF00CB501E /* idevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 198077722E5CA69800CB501E /* idevice.cpp */; };
|
196C574D2EA7F5DC00188F4B /* diagnosticsservice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56BD2EA7F53000188F4B /* diagnosticsservice.cpp */; };
|
||||||
1980779A2E5CA6EF00CB501E /* location_simulation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 198077732E5CA69800CB501E /* location_simulation.cpp */; };
|
196C574E2EA7F5DC00188F4B /* ffi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56BE2EA7F53000188F4B /* ffi.cpp */; };
|
||||||
1980779B2E5CA6EF00CB501E /* lockdown.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 198077742E5CA69800CB501E /* lockdown.cpp */; };
|
196C574F2EA7F5DC00188F4B /* heartbeat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56BF2EA7F53000188F4B /* heartbeat.cpp */; };
|
||||||
1980779C2E5CA6EF00CB501E /* pairing_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 198077752E5CA69800CB501E /* pairing_file.cpp */; };
|
196C57502EA7F5DC00188F4B /* idevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56C02EA7F53000188F4B /* idevice.cpp */; };
|
||||||
1980779D2E5CA6EF00CB501E /* provider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 198077762E5CA69800CB501E /* provider.cpp */; };
|
196C57512EA7F5DC00188F4B /* installation_proxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56C12EA7F53000188F4B /* installation_proxy.cpp */; };
|
||||||
1980779E2E5CA6EF00CB501E /* remote_server.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 198077772E5CA69800CB501E /* remote_server.cpp */; };
|
196C57522EA7F5DC00188F4B /* lockdown.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56C22EA7F53000188F4B /* lockdown.cpp */; };
|
||||||
1980779F2E5CA6EF00CB501E /* rsd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 198077782E5CA69800CB501E /* rsd.cpp */; };
|
196C57532EA7F5DC00188F4B /* mobile_image_mounter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56C32EA7F53000188F4B /* mobile_image_mounter.cpp */; };
|
||||||
198077A02E5CA6EF00CB501E /* tcp_callback_feeder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 198077792E5CA69800CB501E /* tcp_callback_feeder.cpp */; };
|
196C57542EA7F5DC00188F4B /* pairing_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56C42EA7F53000188F4B /* pairing_file.cpp */; };
|
||||||
198077A12E5CA6EF00CB501E /* usbmuxd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1980777A2E5CA69800CB501E /* usbmuxd.cpp */; };
|
196C57552EA7F5DC00188F4B /* provider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56C52EA7F53000188F4B /* provider.cpp */; };
|
||||||
198077B72E5CA6FC00CB501E /* core_device_proxy.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077A62E5CA6FC00CB501E /* core_device_proxy.hpp */; };
|
196C57562EA7F5DC00188F4B /* rsd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56C62EA7F53000188F4B /* rsd.cpp */; };
|
||||||
198077B82E5CA6FC00CB501E /* pairing_file.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077AD2E5CA6FC00CB501E /* pairing_file.hpp */; };
|
196C57572EA7F5DC00188F4B /* tcp_callback_feeder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56C72EA7F53000188F4B /* tcp_callback_feeder.cpp */; };
|
||||||
198077B92E5CA6FC00CB501E /* bindings.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077A52E5CA6FC00CB501E /* bindings.hpp */; };
|
196C57582EA7F5DC00188F4B /* usbmuxd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 196C56C82EA7F53000188F4B /* usbmuxd.cpp */; };
|
||||||
198077BA2E5CA6FC00CB501E /* diagnosticsservice.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077A82E5CA6FC00CB501E /* diagnosticsservice.hpp */; };
|
196C57592EA7F5F400188F4B /* location_simulation.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56DE2EA7F54900188F4B /* location_simulation.hpp */; };
|
||||||
198077BB2E5CA6FC00CB501E /* idevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 198077B52E5CA6FC00CB501E /* idevice.h */; };
|
196C575A2EA7F5F400188F4B /* process_control.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56DF2EA7F54900188F4B /* process_control.hpp */; };
|
||||||
198077BC2E5CA6FC00CB501E /* debug_proxy.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077A72E5CA6FC00CB501E /* debug_proxy.hpp */; };
|
196C575B2EA7F5F400188F4B /* remote_server.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56E02EA7F54900188F4B /* remote_server.hpp */; };
|
||||||
198077BD2E5CA6FC00CB501E /* ffi.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077A92E5CA6FC00CB501E /* ffi.hpp */; };
|
196C575C2EA7F5F400188F4B /* screenshot.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56E12EA7F54900188F4B /* screenshot.hpp */; };
|
||||||
198077BE2E5CA6FC00CB501E /* rsd.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077B12E5CA6FC00CB501E /* rsd.hpp */; };
|
196C575D2EA7F5F400188F4B /* adapter_stream.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56E32EA7F54900188F4B /* adapter_stream.hpp */; };
|
||||||
198077BF2E5CA6FC00CB501E /* tcp_object_stack.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077B22E5CA6FC00CB501E /* tcp_object_stack.hpp */; };
|
196C575E2EA7F5F400188F4B /* app_service.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56E42EA7F54900188F4B /* app_service.hpp */; };
|
||||||
198077C02E5CA6FC00CB501E /* remote_server.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077B02E5CA6FC00CB501E /* remote_server.hpp */; };
|
196C575F2EA7F5F400188F4B /* bindings.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56E52EA7F54900188F4B /* bindings.hpp */; };
|
||||||
198077C12E5CA6FC00CB501E /* readwrite.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077AF2E5CA6FC00CB501E /* readwrite.hpp */; };
|
196C57602EA7F5F400188F4B /* core_device_proxy.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56E62EA7F54900188F4B /* core_device_proxy.hpp */; };
|
||||||
198077C22E5CA6FC00CB501E /* location_simulation.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077AB2E5CA6FC00CB501E /* location_simulation.hpp */; };
|
196C57612EA7F5F400188F4B /* debug_proxy.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56E72EA7F54900188F4B /* debug_proxy.hpp */; };
|
||||||
198077C32E5CA6FC00CB501E /* adapter_stream.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077A32E5CA6FC00CB501E /* adapter_stream.hpp */; };
|
196C57622EA7F5F400188F4B /* diagnosticsservice.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56E82EA7F54900188F4B /* diagnosticsservice.hpp */; };
|
||||||
198077C42E5CA6FC00CB501E /* lockdown.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077AC2E5CA6FC00CB501E /* lockdown.hpp */; };
|
196C57632EA7F5F400188F4B /* ffi.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56E92EA7F54900188F4B /* ffi.hpp */; };
|
||||||
198077C52E5CA6FC00CB501E /* usbmuxd.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077B32E5CA6FC00CB501E /* usbmuxd.hpp */; };
|
196C57642EA7F5F400188F4B /* heartbeat.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56EA2EA7F54900188F4B /* heartbeat.hpp */; };
|
||||||
198077C62E5CA6FC00CB501E /* app_service.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077A42E5CA6FC00CB501E /* app_service.hpp */; };
|
196C57652EA7F5F400188F4B /* idevice.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56EB2EA7F54900188F4B /* idevice.hpp */; };
|
||||||
198077C72E5CA6FC00CB501E /* idevice.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077AA2E5CA6FC00CB501E /* idevice.hpp */; };
|
196C57662EA7F5F400188F4B /* installation_proxy.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56EC2EA7F54900188F4B /* installation_proxy.hpp */; };
|
||||||
198077C82E5CA6FC00CB501E /* provider.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 198077AE2E5CA6FC00CB501E /* provider.hpp */; };
|
196C57672EA7F5F400188F4B /* lockdown.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56ED2EA7F54900188F4B /* lockdown.hpp */; };
|
||||||
|
196C57682EA7F5F400188F4B /* mobile_image_mounter.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56EE2EA7F54900188F4B /* mobile_image_mounter.hpp */; };
|
||||||
|
196C57692EA7F5F400188F4B /* option.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56EF2EA7F54900188F4B /* option.hpp */; };
|
||||||
|
196C576A2EA7F5F400188F4B /* pairing_file.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56F02EA7F54900188F4B /* pairing_file.hpp */; };
|
||||||
|
196C576B2EA7F5F400188F4B /* provider.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56F12EA7F54900188F4B /* provider.hpp */; };
|
||||||
|
196C576C2EA7F5F400188F4B /* readwrite.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56F22EA7F54900188F4B /* readwrite.hpp */; };
|
||||||
|
196C576D2EA7F5F400188F4B /* result.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56F32EA7F54900188F4B /* result.hpp */; };
|
||||||
|
196C576E2EA7F5F400188F4B /* rsd.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56F42EA7F54900188F4B /* rsd.hpp */; };
|
||||||
|
196C576F2EA7F5F400188F4B /* tcp_object_stack.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56F52EA7F54900188F4B /* tcp_object_stack.hpp */; };
|
||||||
|
196C57702EA7F5F400188F4B /* usbmuxd.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 196C56F62EA7F54900188F4B /* usbmuxd.hpp */; };
|
||||||
|
196C57712EA7F5F400188F4B /* idevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 196C56F82EA7F54900188F4B /* idevice.h */; };
|
||||||
198077DF2E5CCA2900CB501E /* libidevice_ffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 198077DB2E5CC33000CB501E /* libidevice_ffi.a */; };
|
198077DF2E5CCA2900CB501E /* libidevice_ffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 198077DB2E5CC33000CB501E /* libidevice_ffi.a */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
1914C7962E623CC2002EAB6E /* option.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = option.hpp; sourceTree = "<group>"; };
|
196C56B42EA7F53000188F4B /* location_simulation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = location_simulation.cpp; sourceTree = "<group>"; };
|
||||||
1914C7982E623CC8002EAB6E /* result.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = result.hpp; sourceTree = "<group>"; };
|
196C56B52EA7F53000188F4B /* process_control.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = process_control.cpp; sourceTree = "<group>"; };
|
||||||
1980776C2E5CA69800CB501E /* adapter_stream.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = adapter_stream.cpp; sourceTree = "<group>"; };
|
196C56B62EA7F53000188F4B /* remote_server.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = remote_server.cpp; sourceTree = "<group>"; };
|
||||||
1980776D2E5CA69800CB501E /* app_service.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = app_service.cpp; sourceTree = "<group>"; };
|
196C56B72EA7F53000188F4B /* screenshot.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = screenshot.cpp; sourceTree = "<group>"; };
|
||||||
1980776E2E5CA69800CB501E /* core_device.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = core_device.cpp; sourceTree = "<group>"; };
|
196C56B92EA7F53000188F4B /* adapter_stream.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = adapter_stream.cpp; sourceTree = "<group>"; };
|
||||||
1980776F2E5CA69800CB501E /* debug_proxy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = debug_proxy.cpp; sourceTree = "<group>"; };
|
196C56BA2EA7F53000188F4B /* app_service.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = app_service.cpp; sourceTree = "<group>"; };
|
||||||
198077702E5CA69800CB501E /* diagnosticsservice.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = diagnosticsservice.cpp; sourceTree = "<group>"; };
|
196C56BB2EA7F53000188F4B /* core_device.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = core_device.cpp; sourceTree = "<group>"; };
|
||||||
198077712E5CA69800CB501E /* ffi.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ffi.cpp; sourceTree = "<group>"; };
|
196C56BC2EA7F53000188F4B /* debug_proxy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = debug_proxy.cpp; sourceTree = "<group>"; };
|
||||||
198077722E5CA69800CB501E /* idevice.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = idevice.cpp; sourceTree = "<group>"; };
|
196C56BD2EA7F53000188F4B /* diagnosticsservice.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = diagnosticsservice.cpp; sourceTree = "<group>"; };
|
||||||
198077732E5CA69800CB501E /* location_simulation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = location_simulation.cpp; sourceTree = "<group>"; };
|
196C56BE2EA7F53000188F4B /* ffi.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ffi.cpp; sourceTree = "<group>"; };
|
||||||
198077742E5CA69800CB501E /* lockdown.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = lockdown.cpp; sourceTree = "<group>"; };
|
196C56BF2EA7F53000188F4B /* heartbeat.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = heartbeat.cpp; sourceTree = "<group>"; };
|
||||||
198077752E5CA69800CB501E /* pairing_file.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = pairing_file.cpp; sourceTree = "<group>"; };
|
196C56C02EA7F53000188F4B /* idevice.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = idevice.cpp; sourceTree = "<group>"; };
|
||||||
198077762E5CA69800CB501E /* provider.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = provider.cpp; sourceTree = "<group>"; };
|
196C56C12EA7F53000188F4B /* installation_proxy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = installation_proxy.cpp; sourceTree = "<group>"; };
|
||||||
198077772E5CA69800CB501E /* remote_server.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = remote_server.cpp; sourceTree = "<group>"; };
|
196C56C22EA7F53000188F4B /* lockdown.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = lockdown.cpp; sourceTree = "<group>"; };
|
||||||
198077782E5CA69800CB501E /* rsd.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = rsd.cpp; sourceTree = "<group>"; };
|
196C56C32EA7F53000188F4B /* mobile_image_mounter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = mobile_image_mounter.cpp; sourceTree = "<group>"; };
|
||||||
198077792E5CA69800CB501E /* tcp_callback_feeder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = tcp_callback_feeder.cpp; sourceTree = "<group>"; };
|
196C56C42EA7F53000188F4B /* pairing_file.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = pairing_file.cpp; sourceTree = "<group>"; };
|
||||||
1980777A2E5CA69800CB501E /* usbmuxd.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = usbmuxd.cpp; sourceTree = "<group>"; };
|
196C56C52EA7F53000188F4B /* provider.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = provider.cpp; sourceTree = "<group>"; };
|
||||||
|
196C56C62EA7F53000188F4B /* rsd.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = rsd.cpp; sourceTree = "<group>"; };
|
||||||
|
196C56C72EA7F53000188F4B /* tcp_callback_feeder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = tcp_callback_feeder.cpp; sourceTree = "<group>"; };
|
||||||
|
196C56C82EA7F53000188F4B /* usbmuxd.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = usbmuxd.cpp; sourceTree = "<group>"; };
|
||||||
|
196C56DE2EA7F54900188F4B /* location_simulation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = location_simulation.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56DF2EA7F54900188F4B /* process_control.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = process_control.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56E02EA7F54900188F4B /* remote_server.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = remote_server.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56E12EA7F54900188F4B /* screenshot.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = screenshot.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56E32EA7F54900188F4B /* adapter_stream.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = adapter_stream.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56E42EA7F54900188F4B /* app_service.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = app_service.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56E52EA7F54900188F4B /* bindings.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = bindings.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56E62EA7F54900188F4B /* core_device_proxy.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = core_device_proxy.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56E72EA7F54900188F4B /* debug_proxy.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = debug_proxy.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56E82EA7F54900188F4B /* diagnosticsservice.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = diagnosticsservice.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56E92EA7F54900188F4B /* ffi.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ffi.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56EA2EA7F54900188F4B /* heartbeat.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = heartbeat.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56EB2EA7F54900188F4B /* idevice.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = idevice.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56EC2EA7F54900188F4B /* installation_proxy.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = installation_proxy.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56ED2EA7F54900188F4B /* lockdown.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = lockdown.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56EE2EA7F54900188F4B /* mobile_image_mounter.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = mobile_image_mounter.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56EF2EA7F54900188F4B /* option.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = option.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56F02EA7F54900188F4B /* pairing_file.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = pairing_file.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56F12EA7F54900188F4B /* provider.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = provider.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56F22EA7F54900188F4B /* readwrite.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = readwrite.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56F32EA7F54900188F4B /* result.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = result.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56F42EA7F54900188F4B /* rsd.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rsd.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56F52EA7F54900188F4B /* tcp_object_stack.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = tcp_object_stack.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56F62EA7F54900188F4B /* usbmuxd.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = usbmuxd.hpp; sourceTree = "<group>"; };
|
||||||
|
196C56F82EA7F54900188F4B /* idevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = idevice.h; sourceTree = "<group>"; };
|
||||||
1980778F2E5CA6C700CB501E /* libidevice++.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libidevice++.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
1980778F2E5CA6C700CB501E /* libidevice++.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libidevice++.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
198077A32E5CA6FC00CB501E /* adapter_stream.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = adapter_stream.hpp; sourceTree = "<group>"; };
|
|
||||||
198077A42E5CA6FC00CB501E /* app_service.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = app_service.hpp; sourceTree = "<group>"; };
|
|
||||||
198077A52E5CA6FC00CB501E /* bindings.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = bindings.hpp; sourceTree = "<group>"; };
|
|
||||||
198077A62E5CA6FC00CB501E /* core_device_proxy.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = core_device_proxy.hpp; sourceTree = "<group>"; };
|
|
||||||
198077A72E5CA6FC00CB501E /* debug_proxy.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = debug_proxy.hpp; sourceTree = "<group>"; };
|
|
||||||
198077A82E5CA6FC00CB501E /* diagnosticsservice.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = diagnosticsservice.hpp; sourceTree = "<group>"; };
|
|
||||||
198077A92E5CA6FC00CB501E /* ffi.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ffi.hpp; sourceTree = "<group>"; };
|
|
||||||
198077AA2E5CA6FC00CB501E /* idevice.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = idevice.hpp; sourceTree = "<group>"; };
|
|
||||||
198077AB2E5CA6FC00CB501E /* location_simulation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = location_simulation.hpp; sourceTree = "<group>"; };
|
|
||||||
198077AC2E5CA6FC00CB501E /* lockdown.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = lockdown.hpp; sourceTree = "<group>"; };
|
|
||||||
198077AD2E5CA6FC00CB501E /* pairing_file.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = pairing_file.hpp; sourceTree = "<group>"; };
|
|
||||||
198077AE2E5CA6FC00CB501E /* provider.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = provider.hpp; sourceTree = "<group>"; };
|
|
||||||
198077AF2E5CA6FC00CB501E /* readwrite.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = readwrite.hpp; sourceTree = "<group>"; };
|
|
||||||
198077B02E5CA6FC00CB501E /* remote_server.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = remote_server.hpp; sourceTree = "<group>"; };
|
|
||||||
198077B12E5CA6FC00CB501E /* rsd.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rsd.hpp; sourceTree = "<group>"; };
|
|
||||||
198077B22E5CA6FC00CB501E /* tcp_object_stack.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = tcp_object_stack.hpp; sourceTree = "<group>"; };
|
|
||||||
198077B32E5CA6FC00CB501E /* usbmuxd.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = usbmuxd.hpp; sourceTree = "<group>"; };
|
|
||||||
198077B52E5CA6FC00CB501E /* idevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = idevice.h; sourceTree = "<group>"; };
|
|
||||||
198077DB2E5CC33000CB501E /* libidevice_ffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libidevice_ffi.a; path = "${DESTINATION_PATH}/libidevice_ffi.a"; sourceTree = "<group>"; };
|
198077DB2E5CC33000CB501E /* libidevice_ffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libidevice_ffi.a; path = "${DESTINATION_PATH}/libidevice_ffi.a"; sourceTree = "<group>"; };
|
||||||
198077E02E5CD49200CB501E /* xcode_build_rust.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = xcode_build_rust.sh; sourceTree = "<group>"; };
|
198077E02E5CD49200CB501E /* xcode_build_rust.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = xcode_build_rust.sh; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
@@ -98,12 +118,95 @@
|
|||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
196C56B82EA7F53000188F4B /* dvt */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
196C56B42EA7F53000188F4B /* location_simulation.cpp */,
|
||||||
|
196C56B52EA7F53000188F4B /* process_control.cpp */,
|
||||||
|
196C56B62EA7F53000188F4B /* remote_server.cpp */,
|
||||||
|
196C56B72EA7F53000188F4B /* screenshot.cpp */,
|
||||||
|
);
|
||||||
|
path = dvt;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
196C56C92EA7F53000188F4B /* src */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
196C56B82EA7F53000188F4B /* dvt */,
|
||||||
|
196C56B92EA7F53000188F4B /* adapter_stream.cpp */,
|
||||||
|
196C56BA2EA7F53000188F4B /* app_service.cpp */,
|
||||||
|
196C56BB2EA7F53000188F4B /* core_device.cpp */,
|
||||||
|
196C56BC2EA7F53000188F4B /* debug_proxy.cpp */,
|
||||||
|
196C56BD2EA7F53000188F4B /* diagnosticsservice.cpp */,
|
||||||
|
196C56BE2EA7F53000188F4B /* ffi.cpp */,
|
||||||
|
196C56BF2EA7F53000188F4B /* heartbeat.cpp */,
|
||||||
|
196C56C02EA7F53000188F4B /* idevice.cpp */,
|
||||||
|
196C56C12EA7F53000188F4B /* installation_proxy.cpp */,
|
||||||
|
196C56C22EA7F53000188F4B /* lockdown.cpp */,
|
||||||
|
196C56C32EA7F53000188F4B /* mobile_image_mounter.cpp */,
|
||||||
|
196C56C42EA7F53000188F4B /* pairing_file.cpp */,
|
||||||
|
196C56C52EA7F53000188F4B /* provider.cpp */,
|
||||||
|
196C56C62EA7F53000188F4B /* rsd.cpp */,
|
||||||
|
196C56C72EA7F53000188F4B /* tcp_callback_feeder.cpp */,
|
||||||
|
196C56C82EA7F53000188F4B /* usbmuxd.cpp */,
|
||||||
|
);
|
||||||
|
path = src;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
196C56E22EA7F54900188F4B /* dvt */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
196C56DE2EA7F54900188F4B /* location_simulation.hpp */,
|
||||||
|
196C56DF2EA7F54900188F4B /* process_control.hpp */,
|
||||||
|
196C56E02EA7F54900188F4B /* remote_server.hpp */,
|
||||||
|
196C56E12EA7F54900188F4B /* screenshot.hpp */,
|
||||||
|
);
|
||||||
|
path = dvt;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
196C56F72EA7F54900188F4B /* idevice++ */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
196C56E22EA7F54900188F4B /* dvt */,
|
||||||
|
196C56E32EA7F54900188F4B /* adapter_stream.hpp */,
|
||||||
|
196C56E42EA7F54900188F4B /* app_service.hpp */,
|
||||||
|
196C56E52EA7F54900188F4B /* bindings.hpp */,
|
||||||
|
196C56E62EA7F54900188F4B /* core_device_proxy.hpp */,
|
||||||
|
196C56E72EA7F54900188F4B /* debug_proxy.hpp */,
|
||||||
|
196C56E82EA7F54900188F4B /* diagnosticsservice.hpp */,
|
||||||
|
196C56E92EA7F54900188F4B /* ffi.hpp */,
|
||||||
|
196C56EA2EA7F54900188F4B /* heartbeat.hpp */,
|
||||||
|
196C56EB2EA7F54900188F4B /* idevice.hpp */,
|
||||||
|
196C56EC2EA7F54900188F4B /* installation_proxy.hpp */,
|
||||||
|
196C56ED2EA7F54900188F4B /* lockdown.hpp */,
|
||||||
|
196C56EE2EA7F54900188F4B /* mobile_image_mounter.hpp */,
|
||||||
|
196C56EF2EA7F54900188F4B /* option.hpp */,
|
||||||
|
196C56F02EA7F54900188F4B /* pairing_file.hpp */,
|
||||||
|
196C56F12EA7F54900188F4B /* provider.hpp */,
|
||||||
|
196C56F22EA7F54900188F4B /* readwrite.hpp */,
|
||||||
|
196C56F32EA7F54900188F4B /* result.hpp */,
|
||||||
|
196C56F42EA7F54900188F4B /* rsd.hpp */,
|
||||||
|
196C56F52EA7F54900188F4B /* tcp_object_stack.hpp */,
|
||||||
|
196C56F62EA7F54900188F4B /* usbmuxd.hpp */,
|
||||||
|
);
|
||||||
|
path = "idevice++";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
196C56F92EA7F54900188F4B /* include */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
196C56F72EA7F54900188F4B /* idevice++ */,
|
||||||
|
196C56F82EA7F54900188F4B /* idevice.h */,
|
||||||
|
);
|
||||||
|
path = include;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
198077562E5CA62F00CB501E = {
|
198077562E5CA62F00CB501E = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
1980777B2E5CA69800CB501E /* src */,
|
196C56C92EA7F53000188F4B /* src */,
|
||||||
|
196C56F92EA7F54900188F4B /* include */,
|
||||||
198077612E5CA62F00CB501E /* Products */,
|
198077612E5CA62F00CB501E /* Products */,
|
||||||
198077B62E5CA6FC00CB501E /* include */,
|
|
||||||
198077D92E5CC31100CB501E /* Frameworks */,
|
198077D92E5CC31100CB501E /* Frameworks */,
|
||||||
198077E02E5CD49200CB501E /* xcode_build_rust.sh */,
|
198077E02E5CD49200CB501E /* xcode_build_rust.sh */,
|
||||||
);
|
);
|
||||||
@@ -117,63 +220,6 @@
|
|||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
1980777B2E5CA69800CB501E /* src */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
1980776C2E5CA69800CB501E /* adapter_stream.cpp */,
|
|
||||||
1980776D2E5CA69800CB501E /* app_service.cpp */,
|
|
||||||
1980776E2E5CA69800CB501E /* core_device.cpp */,
|
|
||||||
1980776F2E5CA69800CB501E /* debug_proxy.cpp */,
|
|
||||||
198077702E5CA69800CB501E /* diagnosticsservice.cpp */,
|
|
||||||
198077712E5CA69800CB501E /* ffi.cpp */,
|
|
||||||
198077722E5CA69800CB501E /* idevice.cpp */,
|
|
||||||
198077732E5CA69800CB501E /* location_simulation.cpp */,
|
|
||||||
198077742E5CA69800CB501E /* lockdown.cpp */,
|
|
||||||
198077752E5CA69800CB501E /* pairing_file.cpp */,
|
|
||||||
198077762E5CA69800CB501E /* provider.cpp */,
|
|
||||||
198077772E5CA69800CB501E /* remote_server.cpp */,
|
|
||||||
198077782E5CA69800CB501E /* rsd.cpp */,
|
|
||||||
198077792E5CA69800CB501E /* tcp_callback_feeder.cpp */,
|
|
||||||
1980777A2E5CA69800CB501E /* usbmuxd.cpp */,
|
|
||||||
);
|
|
||||||
path = src;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
198077B42E5CA6FC00CB501E /* idevice++ */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
198077A32E5CA6FC00CB501E /* adapter_stream.hpp */,
|
|
||||||
198077A42E5CA6FC00CB501E /* app_service.hpp */,
|
|
||||||
198077A52E5CA6FC00CB501E /* bindings.hpp */,
|
|
||||||
198077A62E5CA6FC00CB501E /* core_device_proxy.hpp */,
|
|
||||||
198077A72E5CA6FC00CB501E /* debug_proxy.hpp */,
|
|
||||||
198077A82E5CA6FC00CB501E /* diagnosticsservice.hpp */,
|
|
||||||
198077A92E5CA6FC00CB501E /* ffi.hpp */,
|
|
||||||
198077AA2E5CA6FC00CB501E /* idevice.hpp */,
|
|
||||||
198077AB2E5CA6FC00CB501E /* location_simulation.hpp */,
|
|
||||||
198077AC2E5CA6FC00CB501E /* lockdown.hpp */,
|
|
||||||
198077AD2E5CA6FC00CB501E /* pairing_file.hpp */,
|
|
||||||
198077AE2E5CA6FC00CB501E /* provider.hpp */,
|
|
||||||
198077AF2E5CA6FC00CB501E /* readwrite.hpp */,
|
|
||||||
198077B02E5CA6FC00CB501E /* remote_server.hpp */,
|
|
||||||
198077B12E5CA6FC00CB501E /* rsd.hpp */,
|
|
||||||
198077B22E5CA6FC00CB501E /* tcp_object_stack.hpp */,
|
|
||||||
198077B32E5CA6FC00CB501E /* usbmuxd.hpp */,
|
|
||||||
1914C7962E623CC2002EAB6E /* option.hpp */,
|
|
||||||
1914C7982E623CC8002EAB6E /* result.hpp */,
|
|
||||||
);
|
|
||||||
path = "idevice++";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
198077B62E5CA6FC00CB501E /* include */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
198077B42E5CA6FC00CB501E /* idevice++ */,
|
|
||||||
198077B52E5CA6FC00CB501E /* idevice.h */,
|
|
||||||
);
|
|
||||||
path = include;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
198077D92E5CC31100CB501E /* Frameworks */ = {
|
198077D92E5CC31100CB501E /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -189,26 +235,31 @@
|
|||||||
isa = PBXHeadersBuildPhase;
|
isa = PBXHeadersBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
198077B72E5CA6FC00CB501E /* core_device_proxy.hpp in Headers */,
|
196C57592EA7F5F400188F4B /* location_simulation.hpp in Headers */,
|
||||||
198077B82E5CA6FC00CB501E /* pairing_file.hpp in Headers */,
|
196C575A2EA7F5F400188F4B /* process_control.hpp in Headers */,
|
||||||
198077B92E5CA6FC00CB501E /* bindings.hpp in Headers */,
|
196C575B2EA7F5F400188F4B /* remote_server.hpp in Headers */,
|
||||||
198077BA2E5CA6FC00CB501E /* diagnosticsservice.hpp in Headers */,
|
196C575C2EA7F5F400188F4B /* screenshot.hpp in Headers */,
|
||||||
198077BB2E5CA6FC00CB501E /* idevice.h in Headers */,
|
196C575D2EA7F5F400188F4B /* adapter_stream.hpp in Headers */,
|
||||||
198077BC2E5CA6FC00CB501E /* debug_proxy.hpp in Headers */,
|
196C575E2EA7F5F400188F4B /* app_service.hpp in Headers */,
|
||||||
198077BD2E5CA6FC00CB501E /* ffi.hpp in Headers */,
|
196C575F2EA7F5F400188F4B /* bindings.hpp in Headers */,
|
||||||
198077BE2E5CA6FC00CB501E /* rsd.hpp in Headers */,
|
196C57602EA7F5F400188F4B /* core_device_proxy.hpp in Headers */,
|
||||||
198077BF2E5CA6FC00CB501E /* tcp_object_stack.hpp in Headers */,
|
196C57612EA7F5F400188F4B /* debug_proxy.hpp in Headers */,
|
||||||
198077C02E5CA6FC00CB501E /* remote_server.hpp in Headers */,
|
196C57622EA7F5F400188F4B /* diagnosticsservice.hpp in Headers */,
|
||||||
198077C12E5CA6FC00CB501E /* readwrite.hpp in Headers */,
|
196C57632EA7F5F400188F4B /* ffi.hpp in Headers */,
|
||||||
198077C22E5CA6FC00CB501E /* location_simulation.hpp in Headers */,
|
196C57642EA7F5F400188F4B /* heartbeat.hpp in Headers */,
|
||||||
198077C32E5CA6FC00CB501E /* adapter_stream.hpp in Headers */,
|
196C57652EA7F5F400188F4B /* idevice.hpp in Headers */,
|
||||||
1914C7972E623CC2002EAB6E /* option.hpp in Headers */,
|
196C57662EA7F5F400188F4B /* installation_proxy.hpp in Headers */,
|
||||||
198077C42E5CA6FC00CB501E /* lockdown.hpp in Headers */,
|
196C57672EA7F5F400188F4B /* lockdown.hpp in Headers */,
|
||||||
198077C52E5CA6FC00CB501E /* usbmuxd.hpp in Headers */,
|
196C57682EA7F5F400188F4B /* mobile_image_mounter.hpp in Headers */,
|
||||||
198077C62E5CA6FC00CB501E /* app_service.hpp in Headers */,
|
196C57692EA7F5F400188F4B /* option.hpp in Headers */,
|
||||||
198077C72E5CA6FC00CB501E /* idevice.hpp in Headers */,
|
196C576A2EA7F5F400188F4B /* pairing_file.hpp in Headers */,
|
||||||
198077C82E5CA6FC00CB501E /* provider.hpp in Headers */,
|
196C576B2EA7F5F400188F4B /* provider.hpp in Headers */,
|
||||||
1914C7992E623CC8002EAB6E /* result.hpp in Headers */,
|
196C576C2EA7F5F400188F4B /* readwrite.hpp in Headers */,
|
||||||
|
196C576D2EA7F5F400188F4B /* result.hpp in Headers */,
|
||||||
|
196C576E2EA7F5F400188F4B /* rsd.hpp in Headers */,
|
||||||
|
196C576F2EA7F5F400188F4B /* tcp_object_stack.hpp in Headers */,
|
||||||
|
196C57702EA7F5F400188F4B /* usbmuxd.hpp in Headers */,
|
||||||
|
196C57712EA7F5F400188F4B /* idevice.h in Headers */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -295,21 +346,26 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
198077932E5CA6EF00CB501E /* adapter_stream.cpp in Sources */,
|
196C57452EA7F5DC00188F4B /* location_simulation.cpp in Sources */,
|
||||||
198077942E5CA6EF00CB501E /* app_service.cpp in Sources */,
|
196C57462EA7F5DC00188F4B /* process_control.cpp in Sources */,
|
||||||
198077952E5CA6EF00CB501E /* core_device.cpp in Sources */,
|
196C57472EA7F5DC00188F4B /* remote_server.cpp in Sources */,
|
||||||
198077962E5CA6EF00CB501E /* debug_proxy.cpp in Sources */,
|
196C57482EA7F5DC00188F4B /* screenshot.cpp in Sources */,
|
||||||
198077972E5CA6EF00CB501E /* diagnosticsservice.cpp in Sources */,
|
196C57492EA7F5DC00188F4B /* adapter_stream.cpp in Sources */,
|
||||||
198077982E5CA6EF00CB501E /* ffi.cpp in Sources */,
|
196C574A2EA7F5DC00188F4B /* app_service.cpp in Sources */,
|
||||||
198077992E5CA6EF00CB501E /* idevice.cpp in Sources */,
|
196C574B2EA7F5DC00188F4B /* core_device.cpp in Sources */,
|
||||||
1980779A2E5CA6EF00CB501E /* location_simulation.cpp in Sources */,
|
196C574C2EA7F5DC00188F4B /* debug_proxy.cpp in Sources */,
|
||||||
1980779B2E5CA6EF00CB501E /* lockdown.cpp in Sources */,
|
196C574D2EA7F5DC00188F4B /* diagnosticsservice.cpp in Sources */,
|
||||||
1980779C2E5CA6EF00CB501E /* pairing_file.cpp in Sources */,
|
196C574E2EA7F5DC00188F4B /* ffi.cpp in Sources */,
|
||||||
1980779D2E5CA6EF00CB501E /* provider.cpp in Sources */,
|
196C574F2EA7F5DC00188F4B /* heartbeat.cpp in Sources */,
|
||||||
1980779E2E5CA6EF00CB501E /* remote_server.cpp in Sources */,
|
196C57502EA7F5DC00188F4B /* idevice.cpp in Sources */,
|
||||||
1980779F2E5CA6EF00CB501E /* rsd.cpp in Sources */,
|
196C57512EA7F5DC00188F4B /* installation_proxy.cpp in Sources */,
|
||||||
198077A02E5CA6EF00CB501E /* tcp_callback_feeder.cpp in Sources */,
|
196C57522EA7F5DC00188F4B /* lockdown.cpp in Sources */,
|
||||||
198077A12E5CA6EF00CB501E /* usbmuxd.cpp in Sources */,
|
196C57532EA7F5DC00188F4B /* mobile_image_mounter.cpp in Sources */,
|
||||||
|
196C57542EA7F5DC00188F4B /* pairing_file.cpp in Sources */,
|
||||||
|
196C57552EA7F5DC00188F4B /* provider.cpp in Sources */,
|
||||||
|
196C57562EA7F5DC00188F4B /* rsd.cpp in Sources */,
|
||||||
|
196C57572EA7F5DC00188F4B /* tcp_callback_feeder.cpp in Sources */,
|
||||||
|
196C57582EA7F5DC00188F4B /* usbmuxd.cpp in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -451,11 +507,12 @@
|
|||||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||||
GCC_WARN_UNUSED_LABEL = YES;
|
GCC_WARN_UNUSED_LABEL = YES;
|
||||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||||
|
HEADER_SEARCH_PATHS = "include/**";
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"${TARGET_BUILD_DIR}/**",
|
"${TARGET_BUILD_DIR}/**",
|
||||||
"$(PROJECT_DIR)/${DESTINATION_PATH}",
|
"$(PROJECT_DIR)/${DESTINATION_PATH}",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 15.5;
|
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||||
OTHER_LDFLAGS = "-Wall";
|
OTHER_LDFLAGS = "-Wall";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
@@ -479,11 +536,12 @@
|
|||||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||||
GCC_WARN_UNUSED_LABEL = YES;
|
GCC_WARN_UNUSED_LABEL = YES;
|
||||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||||
|
HEADER_SEARCH_PATHS = "include/**";
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"${TARGET_BUILD_DIR}/**",
|
"${TARGET_BUILD_DIR}/**",
|
||||||
"$(PROJECT_DIR)/${DESTINATION_PATH}",
|
"$(PROJECT_DIR)/${DESTINATION_PATH}",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 15.5;
|
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||||
OTHER_LDFLAGS = "-Wall";
|
OTHER_LDFLAGS = "-Wall";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
|
|||||||
@@ -53,6 +53,14 @@ class Adapter {
|
|||||||
return Ok(ReadWrite::adopt(s));
|
return Ok(ReadWrite::adopt(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<void, FfiError> close() {
|
||||||
|
FfiError e(::adapter_close(handle_.get()));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit Adapter(AdapterHandle* h) noexcept : handle_(h) {}
|
explicit Adapter(AdapterHandle* h) noexcept : handle_(h) {}
|
||||||
AdapterPtr handle_{};
|
AdapterPtr handle_{};
|
||||||
|
|||||||
60
cpp/include/idevice++/diagnostics_relay.hpp
Normal file
60
cpp/include/idevice++/diagnostics_relay.hpp
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <idevice++/bindings.hpp>
|
||||||
|
#include <idevice++/ffi.hpp>
|
||||||
|
#include <idevice++/provider.hpp>
|
||||||
|
#include <idevice++/result.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
using DiagnosticsRelayPtr =
|
||||||
|
std::unique_ptr<DiagnosticsRelayClientHandle,
|
||||||
|
FnDeleter<DiagnosticsRelayClientHandle, diagnostics_relay_client_free>>;
|
||||||
|
|
||||||
|
class DiagnosticsRelay {
|
||||||
|
public:
|
||||||
|
// Factory: connect via Provider
|
||||||
|
static Result<DiagnosticsRelay, FfiError> connect(Provider& provider);
|
||||||
|
|
||||||
|
// Factory: wrap an existing Idevice socket (consumes it on success)
|
||||||
|
static Result<DiagnosticsRelay, FfiError> from_socket(Idevice&& socket);
|
||||||
|
|
||||||
|
// API Methods - queries returning optional plist
|
||||||
|
Result<Option<plist_t>, FfiError> ioregistry(Option<std::string> current_plane,
|
||||||
|
Option<std::string> entry_name,
|
||||||
|
Option<std::string> entry_class) const;
|
||||||
|
|
||||||
|
Result<Option<plist_t>, FfiError> mobilegestalt(Option<std::vector<char*>> keys) const;
|
||||||
|
|
||||||
|
Result<Option<plist_t>, FfiError> gasguage() const;
|
||||||
|
Result<Option<plist_t>, FfiError> nand() const;
|
||||||
|
Result<Option<plist_t>, FfiError> all() const;
|
||||||
|
Result<Option<plist_t>, FfiError> wifi() const;
|
||||||
|
|
||||||
|
// API Methods - actions
|
||||||
|
Result<void, FfiError> restart();
|
||||||
|
Result<void, FfiError> shutdown();
|
||||||
|
Result<void, FfiError> sleep();
|
||||||
|
Result<void, FfiError> goodbye();
|
||||||
|
|
||||||
|
// RAII / moves
|
||||||
|
~DiagnosticsRelay() noexcept = default;
|
||||||
|
DiagnosticsRelay(DiagnosticsRelay&&) noexcept = default;
|
||||||
|
DiagnosticsRelay& operator=(DiagnosticsRelay&&) noexcept = default;
|
||||||
|
DiagnosticsRelay(const DiagnosticsRelay&) = delete;
|
||||||
|
DiagnosticsRelay& operator=(const DiagnosticsRelay&) = delete;
|
||||||
|
|
||||||
|
DiagnosticsRelayClientHandle* raw() const noexcept { return handle_.get(); }
|
||||||
|
static DiagnosticsRelay adopt(DiagnosticsRelayClientHandle* h) noexcept {
|
||||||
|
return DiagnosticsRelay(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit DiagnosticsRelay(DiagnosticsRelayClientHandle* h) noexcept : handle_(h) {}
|
||||||
|
DiagnosticsRelayPtr handle_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace IdeviceFFI
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <idevice++/bindings.hpp>
|
#include <idevice++/bindings.hpp>
|
||||||
#include <idevice++/remote_server.hpp>
|
#include <idevice++/dvt/remote_server.hpp>
|
||||||
#include <idevice++/result.hpp>
|
#include <idevice++/result.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <idevice++/bindings.hpp>
|
#include <idevice++/bindings.hpp>
|
||||||
#include <idevice++/remote_server.hpp>
|
#include <idevice++/dvt/remote_server.hpp>
|
||||||
#include <idevice++/result.hpp>
|
#include <idevice++/result.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
49
cpp/include/idevice++/dvt/screenshot.hpp
Normal file
49
cpp/include/idevice++/dvt/screenshot.hpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <cstring>
|
||||||
|
#include <idevice++/bindings.hpp>
|
||||||
|
#include <idevice++/dvt/remote_server.hpp>
|
||||||
|
#include <idevice++/result.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
using ScreenshotClientPtr =
|
||||||
|
std::unique_ptr<ScreenshotClientHandle,
|
||||||
|
FnDeleter<ScreenshotClientHandle, screenshot_client_free>>;
|
||||||
|
|
||||||
|
/// C++ wrapper around the ScreenshotClient FFI handle
|
||||||
|
///
|
||||||
|
/// Provides a high-level interface for capturing screenshots
|
||||||
|
/// from a connected iOS device through the DVT service.
|
||||||
|
class ScreenshotClient {
|
||||||
|
public:
|
||||||
|
/// Creates a new ScreenshotClient using an existing RemoteServer.
|
||||||
|
///
|
||||||
|
/// The RemoteServer is borrowed, not consumed.
|
||||||
|
static Result<ScreenshotClient, FfiError> create(RemoteServer& server);
|
||||||
|
|
||||||
|
/// Captures a screenshot and returns it as a PNG buffer.
|
||||||
|
///
|
||||||
|
/// On success, returns a vector containing PNG-encoded bytes.
|
||||||
|
Result<std::vector<uint8_t>, FfiError> take_screenshot();
|
||||||
|
|
||||||
|
~ScreenshotClient() noexcept = default;
|
||||||
|
ScreenshotClient(ScreenshotClient&&) noexcept = default;
|
||||||
|
ScreenshotClient& operator=(ScreenshotClient&&) noexcept = default;
|
||||||
|
ScreenshotClient(const ScreenshotClient&) = delete;
|
||||||
|
ScreenshotClient& operator=(const ScreenshotClient&) = delete;
|
||||||
|
|
||||||
|
ScreenshotClientHandle* raw() const noexcept { return handle_.get(); }
|
||||||
|
static ScreenshotClient adopt(ScreenshotClientHandle* h) noexcept {
|
||||||
|
return ScreenshotClient(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit ScreenshotClient(ScreenshotClientHandle* h) noexcept : handle_(h) {}
|
||||||
|
ScreenshotClientPtr handle_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace IdeviceFFI
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <idevice++/ffi.hpp>
|
#include <idevice++/ffi.hpp>
|
||||||
#include <idevice++/provider.hpp>
|
#include <idevice++/provider.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sys/_types/_u_int64_t.h>
|
|
||||||
|
|
||||||
namespace IdeviceFFI {
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ using IdevicePtr = std::unique_ptr<IdeviceHandle, FnDeleter<IdeviceHandle, idevi
|
|||||||
class Idevice {
|
class Idevice {
|
||||||
public:
|
public:
|
||||||
static Result<Idevice, FfiError> create(IdeviceSocketHandle* socket, const std::string& label);
|
static Result<Idevice, FfiError> create(IdeviceSocketHandle* socket, const std::string& label);
|
||||||
|
#if defined(__unix__) || defined(__APPLE__)
|
||||||
|
static Result<Idevice, FfiError> from_fd(int fd, const std::string& label);
|
||||||
|
#endif
|
||||||
|
|
||||||
static Result<Idevice, FfiError>
|
static Result<Idevice, FfiError>
|
||||||
create_tcp(const sockaddr* addr, socklen_t addr_len, const std::string& label);
|
create_tcp(const sockaddr* addr, socklen_t addr_len, const std::string& label);
|
||||||
@@ -39,7 +42,7 @@ class Idevice {
|
|||||||
// Methods
|
// Methods
|
||||||
Result<std::string, FfiError> get_type() const;
|
Result<std::string, FfiError> get_type() const;
|
||||||
Result<void, FfiError> rsd_checkin();
|
Result<void, FfiError> rsd_checkin();
|
||||||
Result<void, FfiError> start_session(const PairingFile& pairing_file);
|
Result<void, FfiError> start_session(const PairingFile& pairing_file, bool legacy);
|
||||||
|
|
||||||
// Ownership/RAII
|
// Ownership/RAII
|
||||||
~Idevice() noexcept = default;
|
~Idevice() noexcept = default;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include <idevice++/ffi.hpp>
|
#include <idevice++/ffi.hpp>
|
||||||
#include <idevice++/provider.hpp>
|
#include <idevice++/provider.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sys/_types/_u_int64_t.h>
|
|
||||||
|
|
||||||
namespace IdeviceFFI {
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
|||||||
@@ -27,43 +27,53 @@
|
|||||||
</ProjectConfiguration>
|
</ProjectConfiguration>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\src\adapter_stream.cpp" />
|
<ClInclude Include="..\..\ffi\idevice.h" />
|
||||||
<ClCompile Include="..\src\app_service.cpp" />
|
|
||||||
<ClCompile Include="..\src\core_device.cpp" />
|
|
||||||
<ClCompile Include="..\src\debug_proxy.cpp" />
|
|
||||||
<ClCompile Include="..\src\diagnosticsservice.cpp" />
|
|
||||||
<ClCompile Include="..\src\ffi.cpp" />
|
|
||||||
<ClCompile Include="..\src\idevice.cpp" />
|
|
||||||
<ClCompile Include="..\src\location_simulation.cpp" />
|
|
||||||
<ClCompile Include="..\src\lockdown.cpp" />
|
|
||||||
<ClCompile Include="..\src\pairing_file.cpp" />
|
|
||||||
<ClCompile Include="..\src\provider.cpp" />
|
|
||||||
<ClCompile Include="..\src\remote_server.cpp" />
|
|
||||||
<ClCompile Include="..\src\rsd.cpp" />
|
|
||||||
<ClCompile Include="..\src\tcp_callback_feeder.cpp" />
|
|
||||||
<ClCompile Include="..\src\usbmuxd.cpp" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClInclude Include="..\include\idevice++\adapter_stream.hpp" />
|
<ClInclude Include="..\include\idevice++\adapter_stream.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\app_service.hpp" />
|
<ClInclude Include="..\include\idevice++\app_service.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\bindings.hpp" />
|
<ClInclude Include="..\include\idevice++\bindings.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\core_device_proxy.hpp" />
|
<ClInclude Include="..\include\idevice++\core_device_proxy.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\debug_proxy.hpp" />
|
<ClInclude Include="..\include\idevice++\debug_proxy.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\diagnosticsservice.hpp" />
|
<ClInclude Include="..\include\idevice++\diagnosticsservice.hpp" />
|
||||||
|
<ClInclude Include="..\include\idevice++\dvt\location_simulation.hpp" />
|
||||||
|
<ClInclude Include="..\include\idevice++\dvt\process_control.hpp" />
|
||||||
|
<ClInclude Include="..\include\idevice++\dvt\remote_server.hpp" />
|
||||||
|
<ClInclude Include="..\include\idevice++\dvt\screenshot.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\ffi.hpp" />
|
<ClInclude Include="..\include\idevice++\ffi.hpp" />
|
||||||
|
<ClInclude Include="..\include\idevice++\heartbeat.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\idevice.hpp" />
|
<ClInclude Include="..\include\idevice++\idevice.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\location_simulation.hpp" />
|
<ClInclude Include="..\include\idevice++\installation_proxy.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\lockdown.hpp" />
|
<ClInclude Include="..\include\idevice++\lockdown.hpp" />
|
||||||
|
<ClInclude Include="..\include\idevice++\mobile_image_mounter.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\option.hpp" />
|
<ClInclude Include="..\include\idevice++\option.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\pairing_file.hpp" />
|
<ClInclude Include="..\include\idevice++\pairing_file.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\provider.hpp" />
|
<ClInclude Include="..\include\idevice++\provider.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\readwrite.hpp" />
|
<ClInclude Include="..\include\idevice++\readwrite.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\remote_server.hpp" />
|
|
||||||
<ClInclude Include="..\include\idevice++\result.hpp" />
|
<ClInclude Include="..\include\idevice++\result.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\rsd.hpp" />
|
<ClInclude Include="..\include\idevice++\rsd.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\tcp_object_stack.hpp" />
|
<ClInclude Include="..\include\idevice++\tcp_object_stack.hpp" />
|
||||||
<ClInclude Include="..\include\idevice++\usbmuxd.hpp" />
|
<ClInclude Include="..\include\idevice++\usbmuxd.hpp" />
|
||||||
<ClInclude Include="..\..\ffi\idevice.h" />
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\src\adapter_stream.cpp" />
|
||||||
|
<ClCompile Include="..\src\app_service.cpp" />
|
||||||
|
<ClCompile Include="..\src\core_device.cpp" />
|
||||||
|
<ClCompile Include="..\src\debug_proxy.cpp" />
|
||||||
|
<ClCompile Include="..\src\diagnosticsservice.cpp" />
|
||||||
|
<ClCompile Include="..\src\dvt\location_simulation.cpp" />
|
||||||
|
<ClCompile Include="..\src\dvt\process_control.cpp" />
|
||||||
|
<ClCompile Include="..\src\dvt\remote_server.cpp" />
|
||||||
|
<ClCompile Include="..\src\dvt\screenshot.cpp" />
|
||||||
|
<ClCompile Include="..\src\ffi.cpp" />
|
||||||
|
<ClCompile Include="..\src\heartbeat.cpp" />
|
||||||
|
<ClCompile Include="..\src\idevice.cpp" />
|
||||||
|
<ClCompile Include="..\src\installation_proxy.cpp" />
|
||||||
|
<ClCompile Include="..\src\lockdown.cpp" />
|
||||||
|
<ClCompile Include="..\src\mobile_image_mounter.cpp" />
|
||||||
|
<ClCompile Include="..\src\pairing_file.cpp" />
|
||||||
|
<ClCompile Include="..\src\provider.cpp" />
|
||||||
|
<ClCompile Include="..\src\rsd.cpp" />
|
||||||
|
<ClCompile Include="..\src\tcp_callback_feeder.cpp" />
|
||||||
|
<ClCompile Include="..\src\usbmuxd.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<VCProjectVersion>17.0</VCProjectVersion>
|
<VCProjectVersion>17.0</VCProjectVersion>
|
||||||
|
|||||||
@@ -18,53 +18,21 @@
|
|||||||
</Filter>
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\src\adapter_stream.cpp">
|
<ClInclude Include="..\..\ffi\idevice.h">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClCompile>
|
</ClInclude>
|
||||||
<ClCompile Include="..\src\app_service.cpp">
|
<ClInclude Include="..\include\idevice++\dvt\location_simulation.hpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Header Files\idevice++</Filter>
|
||||||
</ClCompile>
|
</ClInclude>
|
||||||
<ClCompile Include="..\src\core_device.cpp">
|
<ClInclude Include="..\include\idevice++\dvt\process_control.hpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Header Files\idevice++</Filter>
|
||||||
</ClCompile>
|
</ClInclude>
|
||||||
<ClCompile Include="..\src\debug_proxy.cpp">
|
<ClInclude Include="..\include\idevice++\dvt\remote_server.hpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Header Files\idevice++</Filter>
|
||||||
</ClCompile>
|
</ClInclude>
|
||||||
<ClCompile Include="..\src\diagnosticsservice.cpp">
|
<ClInclude Include="..\include\idevice++\dvt\screenshot.hpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Header Files\idevice++</Filter>
|
||||||
</ClCompile>
|
</ClInclude>
|
||||||
<ClCompile Include="..\src\ffi.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\src\idevice.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\src\location_simulation.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\src\lockdown.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\src\pairing_file.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\src\provider.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\src\remote_server.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\src\rsd.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\src\tcp_callback_feeder.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\src\usbmuxd.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClInclude Include="..\include\idevice++\adapter_stream.hpp">
|
<ClInclude Include="..\include\idevice++\adapter_stream.hpp">
|
||||||
<Filter>Header Files\idevice++</Filter>
|
<Filter>Header Files\idevice++</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -86,15 +54,24 @@
|
|||||||
<ClInclude Include="..\include\idevice++\ffi.hpp">
|
<ClInclude Include="..\include\idevice++\ffi.hpp">
|
||||||
<Filter>Header Files\idevice++</Filter>
|
<Filter>Header Files\idevice++</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\include\idevice++\heartbeat.hpp">
|
||||||
|
<Filter>Header Files\idevice++</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\include\idevice++\idevice.hpp">
|
<ClInclude Include="..\include\idevice++\idevice.hpp">
|
||||||
<Filter>Header Files\idevice++</Filter>
|
<Filter>Header Files\idevice++</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\include\idevice++\location_simulation.hpp">
|
<ClInclude Include="..\include\idevice++\installation_proxy.hpp">
|
||||||
<Filter>Header Files\idevice++</Filter>
|
<Filter>Header Files\idevice++</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\include\idevice++\lockdown.hpp">
|
<ClInclude Include="..\include\idevice++\lockdown.hpp">
|
||||||
<Filter>Header Files\idevice++</Filter>
|
<Filter>Header Files\idevice++</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\include\idevice++\mobile_image_mounter.hpp">
|
||||||
|
<Filter>Header Files\idevice++</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\include\idevice++\option.hpp">
|
||||||
|
<Filter>Header Files\idevice++</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\include\idevice++\pairing_file.hpp">
|
<ClInclude Include="..\include\idevice++\pairing_file.hpp">
|
||||||
<Filter>Header Files\idevice++</Filter>
|
<Filter>Header Files\idevice++</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -104,7 +81,7 @@
|
|||||||
<ClInclude Include="..\include\idevice++\readwrite.hpp">
|
<ClInclude Include="..\include\idevice++\readwrite.hpp">
|
||||||
<Filter>Header Files\idevice++</Filter>
|
<Filter>Header Files\idevice++</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\include\idevice++\remote_server.hpp">
|
<ClInclude Include="..\include\idevice++\result.hpp">
|
||||||
<Filter>Header Files\idevice++</Filter>
|
<Filter>Header Files\idevice++</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\include\idevice++\rsd.hpp">
|
<ClInclude Include="..\include\idevice++\rsd.hpp">
|
||||||
@@ -116,14 +93,67 @@
|
|||||||
<ClInclude Include="..\include\idevice++\usbmuxd.hpp">
|
<ClInclude Include="..\include\idevice++\usbmuxd.hpp">
|
||||||
<Filter>Header Files\idevice++</Filter>
|
<Filter>Header Files\idevice++</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\ffi\idevice.h">
|
</ItemGroup>
|
||||||
<Filter>Header Files</Filter>
|
<ItemGroup>
|
||||||
</ClInclude>
|
<ClCompile Include="..\src\adapter_stream.cpp">
|
||||||
<ClInclude Include="..\include\idevice++\option.hpp">
|
<Filter>Source Files</Filter>
|
||||||
<Filter>Header Files\idevice++</Filter>
|
</ClCompile>
|
||||||
</ClInclude>
|
<ClCompile Include="..\src\app_service.cpp">
|
||||||
<ClInclude Include="..\include\idevice++\result.hpp">
|
<Filter>Source Files</Filter>
|
||||||
<Filter>Header Files\idevice++</Filter>
|
</ClCompile>
|
||||||
</ClInclude>
|
<ClCompile Include="..\src\core_device.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\debug_proxy.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\diagnosticsservice.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\ffi.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\heartbeat.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\idevice.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\installation_proxy.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\lockdown.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\mobile_image_mounter.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\pairing_file.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\provider.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\rsd.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\tcp_callback_feeder.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\usbmuxd.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\dvt\location_simulation.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\dvt\process_control.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\dvt\remote_server.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\dvt\screenshot.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -6,10 +6,11 @@
|
|||||||
namespace IdeviceFFI {
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
Result<void, FfiError> AdapterStream::close() {
|
Result<void, FfiError> AdapterStream::close() {
|
||||||
if (!h_)
|
if (!h_) {
|
||||||
return Ok();
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
FfiError e(::adapter_close(h_));
|
FfiError e(::adapter_stream_close(h_));
|
||||||
if (e) {
|
if (e) {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
@@ -19,8 +20,9 @@ Result<void, FfiError> AdapterStream::close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result<void, FfiError> AdapterStream::send(const uint8_t* data, size_t len) {
|
Result<void, FfiError> AdapterStream::send(const uint8_t* data, size_t len) {
|
||||||
if (!h_)
|
if (!h_) {
|
||||||
return Err(FfiError::NotConnected());
|
return Err(FfiError::NotConnected());
|
||||||
|
}
|
||||||
FfiError e(::adapter_send(h_, data, len));
|
FfiError e(::adapter_send(h_, data, len));
|
||||||
if (e) {
|
if (e) {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
@@ -29,11 +31,13 @@ Result<void, FfiError> AdapterStream::send(const uint8_t *data, size_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result<std::vector<uint8_t>, FfiError> AdapterStream::recv(size_t max_hint) {
|
Result<std::vector<uint8_t>, FfiError> AdapterStream::recv(size_t max_hint) {
|
||||||
if (!h_)
|
if (!h_) {
|
||||||
return Err(FfiError::NotConnected());
|
return Err(FfiError::NotConnected());
|
||||||
|
}
|
||||||
|
|
||||||
if (max_hint == 0)
|
if (max_hint == 0) {
|
||||||
max_hint = 2048;
|
max_hint = 2048;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> out(max_hint);
|
std::vector<uint8_t> out(max_hint);
|
||||||
size_t actual = 0;
|
size_t actual = 0;
|
||||||
|
|||||||
159
cpp/src/diagnostics_relay.cpp
Normal file
159
cpp/src/diagnostics_relay.cpp
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#include <idevice++/bindings.hpp>
|
||||||
|
#include <idevice++/diagnostics_relay.hpp>
|
||||||
|
#include <idevice++/ffi.hpp>
|
||||||
|
#include <idevice++/provider.hpp>
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
// -------- Factory Methods --------
|
||||||
|
|
||||||
|
Result<DiagnosticsRelay, FfiError> DiagnosticsRelay::connect(Provider& provider) {
|
||||||
|
DiagnosticsRelayClientHandle* out = nullptr;
|
||||||
|
FfiError e(::diagnostics_relay_client_connect(provider.raw(), &out));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
return Ok(DiagnosticsRelay::adopt(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<DiagnosticsRelay, FfiError> DiagnosticsRelay::from_socket(Idevice&& socket) {
|
||||||
|
DiagnosticsRelayClientHandle* out = nullptr;
|
||||||
|
FfiError e(::diagnostics_relay_client_new(socket.raw(), &out));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
socket.release();
|
||||||
|
return Ok(DiagnosticsRelay::adopt(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- API Methods --------
|
||||||
|
|
||||||
|
Result<Option<plist_t>, FfiError>
|
||||||
|
DiagnosticsRelay::ioregistry(Option<std::string> current_plane,
|
||||||
|
Option<std::string> entry_name,
|
||||||
|
Option<std::string> entry_class) const {
|
||||||
|
plist_t res = nullptr;
|
||||||
|
|
||||||
|
const char* plane_ptr = current_plane.is_some() ? current_plane.unwrap().c_str() : nullptr;
|
||||||
|
const char* name_ptr = entry_name.is_some() ? entry_name.unwrap().c_str() : nullptr;
|
||||||
|
const char* class_ptr = entry_class.is_some() ? entry_class.unwrap().c_str() : nullptr;
|
||||||
|
|
||||||
|
FfiError e(
|
||||||
|
::diagnostics_relay_client_ioregistry(handle_.get(), plane_ptr, name_ptr, class_ptr, &res));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == nullptr) {
|
||||||
|
return Ok(Option<plist_t>(None));
|
||||||
|
}
|
||||||
|
return Ok(Some(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Option<plist_t>, FfiError>
|
||||||
|
DiagnosticsRelay::mobilegestalt(Option<std::vector<char*>> keys) const {
|
||||||
|
plist_t res = nullptr;
|
||||||
|
|
||||||
|
if (!keys.is_some() || keys.unwrap().empty()) {
|
||||||
|
return Err(FfiError::InvalidArgument());
|
||||||
|
}
|
||||||
|
|
||||||
|
FfiError e(::diagnostics_relay_client_mobilegestalt(
|
||||||
|
handle_.get(), keys.unwrap().data(), keys.unwrap().size(), &res));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == nullptr) {
|
||||||
|
return Ok(Option<plist_t>(None));
|
||||||
|
}
|
||||||
|
return Ok(Some(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Option<plist_t>, FfiError> DiagnosticsRelay::gasguage() const {
|
||||||
|
plist_t res = nullptr;
|
||||||
|
FfiError e(::diagnostics_relay_client_gasguage(handle_.get(), &res));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == nullptr) {
|
||||||
|
return Ok(Option<plist_t>(None));
|
||||||
|
}
|
||||||
|
return Ok(Some(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Option<plist_t>, FfiError> DiagnosticsRelay::nand() const {
|
||||||
|
plist_t res = nullptr;
|
||||||
|
FfiError e(::diagnostics_relay_client_nand(handle_.get(), &res));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == nullptr) {
|
||||||
|
return Ok(Option<plist_t>(None));
|
||||||
|
}
|
||||||
|
return Ok(Some(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Option<plist_t>, FfiError> DiagnosticsRelay::all() const {
|
||||||
|
plist_t res = nullptr;
|
||||||
|
FfiError e(::diagnostics_relay_client_all(handle_.get(), &res));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == nullptr) {
|
||||||
|
return Ok(Option<plist_t>(None));
|
||||||
|
}
|
||||||
|
return Ok(Some(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Option<plist_t>, FfiError> DiagnosticsRelay::wifi() const {
|
||||||
|
plist_t res = nullptr;
|
||||||
|
FfiError e(::diagnostics_relay_client_wifi(handle_.get(), &res));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == nullptr) {
|
||||||
|
return Ok(Option<plist_t>(None));
|
||||||
|
}
|
||||||
|
return Ok(Some(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void, FfiError> DiagnosticsRelay::restart() {
|
||||||
|
FfiError e(::diagnostics_relay_client_restart(handle_.get()));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void, FfiError> DiagnosticsRelay::shutdown() {
|
||||||
|
FfiError e(::diagnostics_relay_client_shutdown(handle_.get()));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void, FfiError> DiagnosticsRelay::sleep() {
|
||||||
|
FfiError e(::diagnostics_relay_client_sleep(handle_.get()));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void, FfiError> DiagnosticsRelay::goodbye() {
|
||||||
|
FfiError e(::diagnostics_relay_client_goodbye(handle_.get()));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace IdeviceFFI
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Jackson Coxson
|
// Jackson Coxson
|
||||||
|
|
||||||
#include <idevice++/location_simulation.hpp>
|
#include <idevice++/dvt/location_simulation.hpp>
|
||||||
|
|
||||||
namespace IdeviceFFI {
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Jackson Coxson
|
// Jackson Coxson
|
||||||
|
|
||||||
#include <idevice++/process_control.hpp>
|
#include <idevice++/dvt/process_control.hpp>
|
||||||
|
|
||||||
namespace IdeviceFFI {
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Jackson Coxson
|
// Jackson Coxson
|
||||||
|
|
||||||
#include <idevice++/remote_server.hpp>
|
#include <idevice++/dvt/remote_server.hpp>
|
||||||
|
|
||||||
namespace IdeviceFFI {
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
37
cpp/src/dvt/screenshot.cpp
Normal file
37
cpp/src/dvt/screenshot.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#include <idevice++/dvt/screenshot.hpp>
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
Result<ScreenshotClient, FfiError> ScreenshotClient::create(RemoteServer& server) {
|
||||||
|
ScreenshotClientHandle* out = nullptr;
|
||||||
|
FfiError e(::screenshot_client_new(server.raw(), &out));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
return Ok(ScreenshotClient::adopt(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<std::vector<uint8_t>, FfiError> ScreenshotClient::take_screenshot() {
|
||||||
|
uint8_t* data = nullptr;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
FfiError e(::screenshot_client_take_screenshot(handle_.get(), &data, &len));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy into a C++ buffer
|
||||||
|
std::vector<uint8_t> out(len);
|
||||||
|
if (len > 0 && data != nullptr) {
|
||||||
|
std::memcpy(out.data(), data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free Rust-allocated data
|
||||||
|
::idevice_data_free(data, len);
|
||||||
|
|
||||||
|
return Ok(std::move(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace IdeviceFFI
|
||||||
@@ -13,6 +13,17 @@ Result<Idevice, FfiError> Idevice::create(IdeviceSocketHandle* socket, const std
|
|||||||
return Ok(Idevice(h));
|
return Ok(Idevice(h));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__unix__) || defined(__APPLE__)
|
||||||
|
Result<Idevice, FfiError> Idevice::from_fd(int fd, const std::string& label) {
|
||||||
|
IdeviceHandle* h = nullptr;
|
||||||
|
FfiError e(idevice_from_fd(fd, label.c_str(), &h));
|
||||||
|
if (e) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
return Ok(Idevice(h));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Result<Idevice, FfiError>
|
Result<Idevice, FfiError>
|
||||||
Idevice::create_tcp(const sockaddr* addr, socklen_t addr_len, const std::string& label) {
|
Idevice::create_tcp(const sockaddr* addr, socklen_t addr_len, const std::string& label) {
|
||||||
IdeviceHandle* h = nullptr;
|
IdeviceHandle* h = nullptr;
|
||||||
@@ -42,8 +53,8 @@ Result<void, FfiError> Idevice::rsd_checkin() {
|
|||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void, FfiError> Idevice::start_session(const PairingFile& pairing_file) {
|
Result<void, FfiError> Idevice::start_session(const PairingFile& pairing_file, bool legacy) {
|
||||||
FfiError e(idevice_start_session(handle_.get(), pairing_file.raw()));
|
FfiError e(idevice_start_session(handle_.get(), pairing_file.raw(), legacy));
|
||||||
if (e) {
|
if (e) {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <idevice++/bindings.hpp>
|
#include <idevice++/bindings.hpp>
|
||||||
#include <idevice++/installation_proxy.hpp>
|
#include <idevice++/installation_proxy.hpp>
|
||||||
#include <sys/_types/_u_int64_t.h>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace IdeviceFFI {
|
namespace IdeviceFFI {
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ Result<std::vector<UsbmuxdDevice>, FfiError> UsbmuxdConnection::get_devices() co
|
|||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
out.emplace_back(UsbmuxdDevice::adopt(list[i]));
|
out.emplace_back(UsbmuxdDevice::adopt(list[i]));
|
||||||
}
|
}
|
||||||
|
idevice_outer_slice_free(list, count);
|
||||||
return Ok(std::move(out));
|
return Ok(std::move(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
idevice = { path = "../idevice", default-features = false }
|
idevice = { path = "../idevice", default-features = false }
|
||||||
futures = { version = "0.3", optional = true }
|
futures = { version = "0.3", optional = true }
|
||||||
log = "0.4.26"
|
tracing = { version = "0.1.41" }
|
||||||
simplelog = "0.12.2"
|
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
|
||||||
|
tracing-appender = { version = "0.2" }
|
||||||
once_cell = "1.21.1"
|
once_cell = "1.21.1"
|
||||||
tokio = { version = "1.44.1", features = ["full"] }
|
tokio = { version = "1.44.1", features = ["full"] }
|
||||||
libc = "0.2.171"
|
libc = "0.2.171"
|
||||||
@@ -17,7 +18,7 @@ plist_ffi = { version = "0.1.6" }
|
|||||||
uuid = { version = "1.12", features = ["v4"], optional = true }
|
uuid = { version = "1.12", features = ["v4"], optional = true }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
windows-sys = { version = "0.60", features = ["Win32_Networking_WinSock"] }
|
windows-sys = { version = "0.61", features = ["Win32_Networking_WinSock"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
aws-lc = ["idevice/aws-lc"]
|
aws-lc = ["idevice/aws-lc"]
|
||||||
@@ -30,6 +31,7 @@ core_device = ["idevice/core_device", "dep:futures", "dep:uuid"]
|
|||||||
core_device_proxy = ["idevice/core_device_proxy"]
|
core_device_proxy = ["idevice/core_device_proxy"]
|
||||||
crashreportcopymobile = ["idevice/crashreportcopymobile"]
|
crashreportcopymobile = ["idevice/crashreportcopymobile"]
|
||||||
debug_proxy = ["idevice/debug_proxy"]
|
debug_proxy = ["idevice/debug_proxy"]
|
||||||
|
diagnostics_relay = ["idevice/diagnostics_relay"]
|
||||||
dvt = ["idevice/dvt"]
|
dvt = ["idevice/dvt"]
|
||||||
heartbeat = ["idevice/heartbeat"]
|
heartbeat = ["idevice/heartbeat"]
|
||||||
house_arrest = ["idevice/house_arrest"]
|
house_arrest = ["idevice/house_arrest"]
|
||||||
@@ -48,6 +50,7 @@ tss = ["idevice/tss"]
|
|||||||
tunneld = ["idevice/tunneld"]
|
tunneld = ["idevice/tunneld"]
|
||||||
usbmuxd = ["idevice/usbmuxd"]
|
usbmuxd = ["idevice/usbmuxd"]
|
||||||
xpc = ["idevice/xpc"]
|
xpc = ["idevice/xpc"]
|
||||||
|
screenshotr = ["idevice/screenshotr"]
|
||||||
full = [
|
full = [
|
||||||
"afc",
|
"afc",
|
||||||
"amfi",
|
"amfi",
|
||||||
@@ -55,6 +58,7 @@ full = [
|
|||||||
"core_device_proxy",
|
"core_device_proxy",
|
||||||
"crashreportcopymobile",
|
"crashreportcopymobile",
|
||||||
"debug_proxy",
|
"debug_proxy",
|
||||||
|
"diagnostics_relay",
|
||||||
"dvt",
|
"dvt",
|
||||||
"heartbeat",
|
"heartbeat",
|
||||||
"house_arrest",
|
"house_arrest",
|
||||||
@@ -72,6 +76,7 @@ full = [
|
|||||||
"tunneld",
|
"tunneld",
|
||||||
"springboardservices",
|
"springboardservices",
|
||||||
"syslog_relay",
|
"syslog_relay",
|
||||||
|
"screenshotr",
|
||||||
]
|
]
|
||||||
default = ["full", "aws-lc"]
|
default = ["full", "aws-lc"]
|
||||||
|
|
||||||
@@ -80,4 +85,4 @@ cbindgen = "0.29.0"
|
|||||||
ureq = "3"
|
ureq = "3"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["staticlib", "cdylib"]
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|||||||
15
ffi/build.rs
15
ffi/build.rs
@@ -35,16 +35,25 @@ fn main() {
|
|||||||
.expect("Unable to generate bindings")
|
.expect("Unable to generate bindings")
|
||||||
.write_to_file("idevice.h");
|
.write_to_file("idevice.h");
|
||||||
|
|
||||||
|
// Check if plist.h exists locally first, otherwise download
|
||||||
|
let plist_h_path = "plist.h";
|
||||||
|
let h = if std::path::Path::new(plist_h_path).exists() {
|
||||||
|
std::fs::read_to_string(plist_h_path).expect("failed to read plist.h")
|
||||||
|
} else {
|
||||||
// download plist.h
|
// download plist.h
|
||||||
let h = ureq::get("https://raw.githubusercontent.com/libimobiledevice/libplist/refs/heads/master/include/plist/plist.h")
|
let h = ureq::get("https://raw.githubusercontent.com/libimobiledevice/libplist/refs/heads/master/include/plist/plist.h")
|
||||||
.call()
|
.call()
|
||||||
.expect("failed to download plist.h");
|
.expect("failed to download plist.h");
|
||||||
let h = h
|
h.into_body()
|
||||||
.into_body()
|
|
||||||
.read_to_string()
|
.read_to_string()
|
||||||
.expect("failed to get string content");
|
.expect("failed to get string content")
|
||||||
|
};
|
||||||
|
|
||||||
let mut f = OpenOptions::new().append(true).open("idevice.h").unwrap();
|
let mut f = OpenOptions::new().append(true).open("idevice.h").unwrap();
|
||||||
f.write_all(b"\n\n\n").unwrap();
|
f.write_all(b"\n\n\n").unwrap();
|
||||||
f.write_all(&h.into_bytes())
|
f.write_all(&h.into_bytes())
|
||||||
.expect("failed to append plist.h");
|
.expect("failed to append plist.h");
|
||||||
|
|
||||||
|
let f = std::fs::read_to_string("idevice.h").unwrap();
|
||||||
|
std::fs::write("../cpp/include/idevice.h", f).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ int main(int argc, char **argv) {
|
|||||||
} else {
|
} else {
|
||||||
uint8_t *data = NULL;
|
uint8_t *data = NULL;
|
||||||
size_t length = 0;
|
size_t length = 0;
|
||||||
err = afc_file_read(file, &data, &length);
|
err = afc_file_read_entire(file, &data, &length);
|
||||||
if (err == NULL) {
|
if (err == NULL) {
|
||||||
if (write_file(dest_path, data, length)) {
|
if (write_file(dest_path, data, length)) {
|
||||||
printf("File downloaded successfully\n");
|
printf("File downloaded successfully\n");
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
// Connect to installation proxy
|
// Connect to installation proxy
|
||||||
InstallationProxyClientHandle *instproxy_client = NULL;
|
InstallationProxyClientHandle *instproxy_client = NULL;
|
||||||
err = installation_proxy_connect_tcp(provider, &instproxy_client);
|
err = installation_proxy_connect(provider, &instproxy_client);
|
||||||
if (err != NULL) {
|
if (err != NULL) {
|
||||||
fprintf(stderr, "Failed to connect to installation proxy: [%d] %s",
|
fprintf(stderr, "Failed to connect to installation proxy: [%d] %s",
|
||||||
err->code, err->message);
|
err->code, err->message);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ int main() {
|
|||||||
|
|
||||||
// Connect to installation proxy
|
// Connect to installation proxy
|
||||||
InstallationProxyClientHandle *client = NULL;
|
InstallationProxyClientHandle *client = NULL;
|
||||||
err = installation_proxy_connect_tcp(provider, &client);
|
err = installation_proxy_connect(provider, &client);
|
||||||
if (err != NULL) {
|
if (err != NULL) {
|
||||||
fprintf(stderr, "Failed to connect to installation proxy: [%d] %s",
|
fprintf(stderr, "Failed to connect to installation proxy: [%d] %s",
|
||||||
err->code, err->message);
|
err->code, err->message);
|
||||||
|
|||||||
@@ -131,7 +131,6 @@ int main(int argc, char **argv) {
|
|||||||
fprintf(stderr, "Failed to connect to image mounter: [%d] %s", err->code,
|
fprintf(stderr, "Failed to connect to image mounter: [%d] %s", err->code,
|
||||||
err->message);
|
err->message);
|
||||||
idevice_error_free(err);
|
idevice_error_free(err);
|
||||||
idevice_provider_free(provider);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
idevice_provider_free(provider);
|
idevice_provider_free(provider);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use idevice::tcp::handle::StreamHandle;
|
|||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
|
||||||
use crate::core_device_proxy::AdapterHandle;
|
use crate::core_device_proxy::AdapterHandle;
|
||||||
use crate::{IdeviceFfiError, RUNTIME, ReadWriteOpaque, ffi_err};
|
use crate::{IdeviceFfiError, ReadWriteOpaque, ffi_err, run_sync};
|
||||||
|
|
||||||
pub struct AdapterStreamHandle(pub StreamHandle);
|
pub struct AdapterStreamHandle(pub StreamHandle);
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ pub unsafe extern "C" fn adapter_connect(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let adapter = unsafe { &mut (*adapter_handle).0 };
|
let adapter = unsafe { &mut (*adapter_handle).0 };
|
||||||
let res = RUNTIME.block_on(async move { adapter.connect(port).await });
|
let res = run_sync(async move { adapter.connect(port).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(r) => {
|
Ok(r) => {
|
||||||
@@ -47,7 +47,7 @@ pub unsafe extern "C" fn adapter_connect(
|
|||||||
null_mut()
|
null_mut()
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Adapter connect failed: {e}");
|
tracing::error!("Adapter connect failed: {e}");
|
||||||
ffi_err!(e)
|
ffi_err!(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,12 +81,12 @@ pub unsafe extern "C" fn adapter_pcap(
|
|||||||
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
|
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async move { adapter.pcap(path_str).await });
|
let res = run_sync(async move { adapter.pcap(path_str).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Adapter pcap failed: {e}");
|
tracing::error!("Adapter pcap failed: {e}");
|
||||||
ffi_err!(e)
|
ffi_err!(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,13 +103,37 @@ pub unsafe extern "C" fn adapter_pcap(
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
/// `handle` must be a valid pointer to a handle allocated by this library
|
/// `handle` must be a valid pointer to a handle allocated by this library
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn adapter_close(handle: *mut AdapterStreamHandle) -> *mut IdeviceFfiError {
|
pub unsafe extern "C" fn adapter_stream_close(
|
||||||
|
handle: *mut AdapterStreamHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
if handle.is_null() {
|
if handle.is_null() {
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let adapter = unsafe { &mut (*handle).0 };
|
let adapter = unsafe { &mut (*handle).0 };
|
||||||
RUNTIME.block_on(async move { adapter.close() });
|
run_sync(async move { adapter.close() });
|
||||||
|
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops the entire adapter TCP stack
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`handle`] - The adapter handle
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Null on success, an IdeviceFfiError otherwise
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `handle` must be a valid pointer to a handle allocated by this library
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn adapter_close(handle: *mut AdapterHandle) -> *mut IdeviceFfiError {
|
||||||
|
if handle.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let adapter = unsafe { &mut (*handle).0 };
|
||||||
|
run_sync(async move { adapter.close().await.ok() });
|
||||||
|
|
||||||
null_mut()
|
null_mut()
|
||||||
}
|
}
|
||||||
@@ -140,12 +164,12 @@ pub unsafe extern "C" fn adapter_send(
|
|||||||
let adapter = unsafe { &mut (*handle).0 };
|
let adapter = unsafe { &mut (*handle).0 };
|
||||||
let data_slice = unsafe { std::slice::from_raw_parts(data, length) };
|
let data_slice = unsafe { std::slice::from_raw_parts(data, length) };
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async move { adapter.write_all(data_slice).await });
|
let res = run_sync(async move { adapter.write_all(data_slice).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Adapter send failed: {e}");
|
tracing::error!("Adapter send failed: {e}");
|
||||||
ffi_err!(e)
|
ffi_err!(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,7 +202,7 @@ pub unsafe extern "C" fn adapter_recv(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let adapter = unsafe { &mut (*handle).0 };
|
let adapter = unsafe { &mut (*handle).0 };
|
||||||
let res: Result<Vec<u8>, std::io::Error> = RUNTIME.block_on(async move {
|
let res: Result<Vec<u8>, std::io::Error> = run_sync(async move {
|
||||||
let mut buf = [0; 2048];
|
let mut buf = [0; 2048];
|
||||||
let res = adapter.read(&mut buf).await?;
|
let res = adapter.read(&mut buf).await?;
|
||||||
Ok(buf[..res].to_vec())
|
Ok(buf[..res].to_vec())
|
||||||
@@ -199,7 +223,7 @@ pub unsafe extern "C" fn adapter_recv(
|
|||||||
null_mut()
|
null_mut()
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Adapter recv failed: {e}");
|
tracing::error!("Adapter recv failed: {e}");
|
||||||
ffi_err!(e)
|
ffi_err!(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
226
ffi/src/afc.rs
226
ffi/src/afc.rs
@@ -1,14 +1,18 @@
|
|||||||
// Jackson Coxson
|
// Jackson Coxson
|
||||||
|
|
||||||
use std::ptr::null_mut;
|
use std::{io::SeekFrom, ptr::null_mut};
|
||||||
|
|
||||||
use idevice::{
|
use idevice::{
|
||||||
IdeviceError, IdeviceService,
|
IdeviceError, IdeviceService,
|
||||||
afc::{AfcClient, DeviceInfo, FileInfo},
|
afc::{AfcClient, DeviceInfo, FileInfo, file::FileDescriptor},
|
||||||
provider::IdeviceProvider,
|
provider::IdeviceProvider,
|
||||||
};
|
};
|
||||||
|
use tokio::io::{AsyncReadExt, AsyncSeekExt};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, IdeviceHandle, RUNTIME, ffi_err, provider::IdeviceProviderHandle};
|
use crate::{
|
||||||
|
IdeviceFfiError, IdeviceHandle, LOCAL_RUNTIME, ffi_err, provider::IdeviceProviderHandle,
|
||||||
|
run_sync, run_sync_local,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct AfcClientHandle(pub AfcClient);
|
pub struct AfcClientHandle(pub AfcClient);
|
||||||
|
|
||||||
@@ -30,11 +34,11 @@ pub unsafe extern "C" fn afc_client_connect(
|
|||||||
client: *mut *mut AfcClientHandle,
|
client: *mut *mut AfcClientHandle,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if provider.is_null() || client.is_null() {
|
if provider.is_null() || client.is_null() {
|
||||||
log::error!("Null pointer provided");
|
tracing::error!("Null pointer provided");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async {
|
let res = run_sync_local(async {
|
||||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
|
|
||||||
AfcClient::connect(provider_ref).await
|
AfcClient::connect(provider_ref).await
|
||||||
@@ -50,6 +54,44 @@ pub unsafe extern "C" fn afc_client_connect(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Connects to the AFC2 service using a TCP provider
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`provider`] - An IdeviceProvider
|
||||||
|
/// * [`client`] - On success, will be set to point to a newly allocated AfcClient handle
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `provider` must be a valid pointer to a handle allocated by this library
|
||||||
|
/// `client` must be a valid, non-null pointer to a location where the handle will be stored
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn afc2_client_connect(
|
||||||
|
provider: *mut IdeviceProviderHandle,
|
||||||
|
client: *mut *mut AfcClientHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if provider.is_null() || client.is_null() {
|
||||||
|
tracing::error!("Null pointer provided");
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = run_sync_local(async {
|
||||||
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
|
|
||||||
|
AfcClient::new_afc2(provider_ref).await
|
||||||
|
});
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(r) => {
|
||||||
|
let boxed = Box::new(AfcClientHandle(r));
|
||||||
|
unsafe { *client = Box::into_raw(boxed) };
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new AfcClient from an existing Idevice connection
|
/// Creates a new AfcClient from an existing Idevice connection
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -88,7 +130,7 @@ pub unsafe extern "C" fn afc_client_new(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn afc_client_free(handle: *mut AfcClientHandle) {
|
pub unsafe extern "C" fn afc_client_free(handle: *mut AfcClientHandle) {
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
log::debug!("Freeing afc_client");
|
tracing::debug!("Freeing afc_client");
|
||||||
let _ = unsafe { Box::from_raw(handle) };
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,7 +164,7 @@ pub unsafe extern "C" fn afc_list_directory(
|
|||||||
// Use to_string_lossy to handle non-UTF8 paths
|
// Use to_string_lossy to handle non-UTF8 paths
|
||||||
let path = path_cstr.to_string_lossy();
|
let path = path_cstr.to_string_lossy();
|
||||||
|
|
||||||
let res: Result<Vec<String>, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<Vec<String>, IdeviceError> = run_sync_local(async move {
|
||||||
// SAFETY: We're assuming client is a valid pointer here
|
// SAFETY: We're assuming client is a valid pointer here
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.list_dir(&path.to_string()).await
|
client_ref.list_dir(&path.to_string()).await
|
||||||
@@ -194,7 +236,7 @@ pub unsafe extern "C" fn afc_make_directory(
|
|||||||
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.mk_dir(path).await
|
client_ref.mk_dir(path).await
|
||||||
});
|
});
|
||||||
@@ -246,7 +288,7 @@ pub unsafe extern "C" fn afc_get_file_info(
|
|||||||
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<FileInfo, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<FileInfo, IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.get_file_info(path).await
|
client_ref.get_file_info(path).await
|
||||||
});
|
});
|
||||||
@@ -331,7 +373,7 @@ pub unsafe extern "C" fn afc_get_device_info(
|
|||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<DeviceInfo, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<DeviceInfo, IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.get_device_info().await
|
client_ref.get_device_info().await
|
||||||
});
|
});
|
||||||
@@ -395,7 +437,7 @@ pub unsafe extern "C" fn afc_remove_path(
|
|||||||
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.remove(path).await
|
client_ref.remove(path).await
|
||||||
});
|
});
|
||||||
@@ -433,7 +475,7 @@ pub unsafe extern "C" fn afc_remove_path_and_contents(
|
|||||||
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.remove_all(path).await
|
client_ref.remove_all(path).await
|
||||||
});
|
});
|
||||||
@@ -506,7 +548,7 @@ pub unsafe extern "C" fn afc_file_open(
|
|||||||
|
|
||||||
let mode = mode.into();
|
let mode = mode.into();
|
||||||
|
|
||||||
let res: Result<*mut AfcFileHandle, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<*mut AfcFileHandle, IdeviceError> = LOCAL_RUNTIME.block_on(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
let result = client_ref.open(path, mode).await;
|
let result = client_ref.open(path, mode).await;
|
||||||
match result {
|
match result {
|
||||||
@@ -544,7 +586,7 @@ pub unsafe extern "C" fn afc_file_close(handle: *mut AfcFileHandle) -> *mut Idev
|
|||||||
}
|
}
|
||||||
|
|
||||||
let fd = unsafe { Box::from_raw(handle as *mut idevice::afc::file::FileDescriptor) };
|
let fd = unsafe { Box::from_raw(handle as *mut idevice::afc::file::FileDescriptor) };
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { fd.close().await });
|
let res: Result<(), IdeviceError> = run_sync(async move { fd.close().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
@@ -552,12 +594,13 @@ pub unsafe extern "C" fn afc_file_close(handle: *mut AfcFileHandle) -> *mut Idev
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads data from an open file
|
/// Reads data from an open file. This advances the cursor of the file.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * [`handle`] - File handle to read from
|
/// * [`handle`] - File handle to read from
|
||||||
/// * [`data`] - Will be set to point to the read data
|
/// * [`data`] - Will be set to point to the read data
|
||||||
/// * [`length`] - Will be set to the length of the read data
|
/// * [`len`] - Number of bytes to read from the file
|
||||||
|
/// * [`bytes_read`] - The number of bytes read from the file
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// An IdeviceFfiError on error, null on success
|
/// An IdeviceFfiError on error, null on success
|
||||||
@@ -566,6 +609,53 @@ pub unsafe extern "C" fn afc_file_close(handle: *mut AfcFileHandle) -> *mut Idev
|
|||||||
/// All pointers must be valid and non-null
|
/// All pointers must be valid and non-null
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn afc_file_read(
|
pub unsafe extern "C" fn afc_file_read(
|
||||||
|
handle: *mut AfcFileHandle,
|
||||||
|
data: *mut *mut u8,
|
||||||
|
len: usize,
|
||||||
|
bytes_read: *mut libc::size_t,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if handle.is_null() || data.is_null() || bytes_read.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let fd = unsafe { &mut *(handle as *mut FileDescriptor) };
|
||||||
|
let res: Result<Vec<u8>, IdeviceError> = run_sync({
|
||||||
|
let mut buf = vec![0u8; len];
|
||||||
|
async move {
|
||||||
|
let r = fd.read(&mut buf).await?;
|
||||||
|
buf.truncate(r);
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(bytes) => {
|
||||||
|
let mut boxed = bytes.into_boxed_slice();
|
||||||
|
unsafe {
|
||||||
|
*data = boxed.as_mut_ptr();
|
||||||
|
*bytes_read = boxed.len();
|
||||||
|
}
|
||||||
|
std::mem::forget(boxed);
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads all data from an open file.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`handle`] - File handle to read from
|
||||||
|
/// * [`data`] - Will be set to point to the read data
|
||||||
|
/// * [`length`] - The number of bytes read from the file
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// All pointers must be valid and non-null
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn afc_file_read_entire(
|
||||||
handle: *mut AfcFileHandle,
|
handle: *mut AfcFileHandle,
|
||||||
data: *mut *mut u8,
|
data: *mut *mut u8,
|
||||||
length: *mut libc::size_t,
|
length: *mut libc::size_t,
|
||||||
@@ -575,7 +665,7 @@ pub unsafe extern "C" fn afc_file_read(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let fd = unsafe { &mut *(handle as *mut idevice::afc::file::FileDescriptor) };
|
let fd = unsafe { &mut *(handle as *mut idevice::afc::file::FileDescriptor) };
|
||||||
let res: Result<Vec<u8>, IdeviceError> = RUNTIME.block_on(async move { fd.read().await });
|
let res: Result<Vec<u8>, IdeviceError> = run_sync(async move { fd.read_entire().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(bytes) => {
|
Ok(bytes) => {
|
||||||
@@ -591,6 +681,100 @@ pub unsafe extern "C" fn afc_file_read(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Moves the read/write cursor in an open file.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`handle`] - File handle whose cursor should be moved
|
||||||
|
/// * [`offset`] - Distance to move the cursor, interpreted based on `whence`
|
||||||
|
/// * [`whence`] - Origin used for the seek operation:
|
||||||
|
/// * `0` — Seek from the start of the file (`SeekFrom::Start`)
|
||||||
|
/// * `1` — Seek from the current cursor position (`SeekFrom::Current`)
|
||||||
|
/// * `2` — Seek from the end of the file (`SeekFrom::End`)
|
||||||
|
/// * [`new_pos`] - Output parameter; will be set to the new absolute cursor position
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An [`IdeviceFfiError`] on error, or null on success.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// All pointers must be valid and non-null.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// * If `whence` is invalid, this function returns `FfiInvalidArg`.
|
||||||
|
/// * The AFC protocol may restrict seeking beyond certain bounds; such errors
|
||||||
|
/// are reported through the returned [`IdeviceFfiError`].
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn afc_file_seek(
|
||||||
|
handle: *mut AfcFileHandle,
|
||||||
|
offset: i64,
|
||||||
|
whence: libc::c_int,
|
||||||
|
new_pos: *mut i64,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if handle.is_null() || new_pos.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let fd = unsafe { &mut *(handle as *mut FileDescriptor) };
|
||||||
|
|
||||||
|
let seek_from = match whence {
|
||||||
|
0 => SeekFrom::Start(offset as u64),
|
||||||
|
1 => SeekFrom::Current(offset),
|
||||||
|
2 => SeekFrom::End(offset),
|
||||||
|
_ => return ffi_err!(IdeviceError::FfiInvalidArg),
|
||||||
|
};
|
||||||
|
|
||||||
|
let res: Result<u64, IdeviceError> = run_sync(async move { Ok(fd.seek(seek_from).await?) });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(pos) => {
|
||||||
|
unsafe {
|
||||||
|
*new_pos = pos as i64;
|
||||||
|
}
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current read/write cursor position of an open file.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`handle`] - File handle whose cursor should be queried
|
||||||
|
/// * [`pos`] - Output parameter; will be set to the current absolute cursor position
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An [`IdeviceFfiError`] on error, or null on success.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// All pointers must be valid and non-null.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// This function is equivalent to performing a seek operation with
|
||||||
|
/// `SeekFrom::Current(0)` internally.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn afc_file_tell(
|
||||||
|
handle: *mut AfcFileHandle,
|
||||||
|
pos: *mut i64,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if handle.is_null() || pos.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let fd = unsafe { &mut *(handle as *mut FileDescriptor) };
|
||||||
|
|
||||||
|
let res: Result<u64, IdeviceError> =
|
||||||
|
run_sync(async { Ok(fd.seek(SeekFrom::Current(0)).await?) });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(cur) => {
|
||||||
|
unsafe {
|
||||||
|
*pos = cur as i64;
|
||||||
|
}
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Writes data to an open file
|
/// Writes data to an open file
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -617,7 +801,7 @@ pub unsafe extern "C" fn afc_file_write(
|
|||||||
let fd = unsafe { &mut *(handle as *mut idevice::afc::file::FileDescriptor) };
|
let fd = unsafe { &mut *(handle as *mut idevice::afc::file::FileDescriptor) };
|
||||||
let data_slice = unsafe { std::slice::from_raw_parts(data, length) };
|
let data_slice = unsafe { std::slice::from_raw_parts(data, length) };
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { fd.write(data_slice).await });
|
let res: Result<(), IdeviceError> = run_sync(async move { fd.write_entire(data_slice).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
@@ -674,7 +858,7 @@ pub unsafe extern "C" fn afc_make_link(
|
|||||||
AfcLinkType::Symbolic => idevice::afc::opcode::LinkType::Symlink,
|
AfcLinkType::Symbolic => idevice::afc::opcode::LinkType::Symlink,
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.link(target, source, link_type).await
|
client_ref.link(target, source, link_type).await
|
||||||
});
|
});
|
||||||
@@ -720,7 +904,7 @@ pub unsafe extern "C" fn afc_rename_path(
|
|||||||
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.rename(source, target).await
|
client_ref.rename(source, target).await
|
||||||
});
|
});
|
||||||
@@ -741,7 +925,7 @@ pub unsafe extern "C" fn afc_rename_path(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn afc_file_read_data_free(data: *mut u8, length: libc::size_t) {
|
pub unsafe extern "C" fn afc_file_read_data_free(data: *mut u8, length: libc::size_t) {
|
||||||
if !data.is_null() {
|
if !data.is_null() {
|
||||||
let boxed = unsafe { Box::from_raw(std::slice::from_raw_parts_mut(data, length)) };
|
let boxed = unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(data, length)) };
|
||||||
drop(boxed);
|
drop(boxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ use std::ptr::null_mut;
|
|||||||
|
|
||||||
use idevice::{IdeviceError, IdeviceService, amfi::AmfiClient, provider::IdeviceProvider};
|
use idevice::{IdeviceError, IdeviceService, amfi::AmfiClient, provider::IdeviceProvider};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, IdeviceHandle, RUNTIME, ffi_err, provider::IdeviceProviderHandle};
|
use crate::{
|
||||||
|
IdeviceFfiError, IdeviceHandle, ffi_err, provider::IdeviceProviderHandle, run_sync_local,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct AmfiClientHandle(pub AmfiClient);
|
pub struct AmfiClientHandle(pub AmfiClient);
|
||||||
|
|
||||||
@@ -26,11 +28,11 @@ pub unsafe extern "C" fn amfi_connect(
|
|||||||
client: *mut *mut AmfiClientHandle,
|
client: *mut *mut AmfiClientHandle,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if provider.is_null() || client.is_null() {
|
if provider.is_null() || client.is_null() {
|
||||||
log::error!("Null pointer provided");
|
tracing::error!("Null pointer provided");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<AmfiClient, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<AmfiClient, IdeviceError> = run_sync_local(async move {
|
||||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
|
|
||||||
// Connect using the reference
|
// Connect using the reference
|
||||||
@@ -94,7 +96,7 @@ pub unsafe extern "C" fn amfi_reveal_developer_mode_option_in_ui(
|
|||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.reveal_developer_mode_option_in_ui().await
|
client_ref.reveal_developer_mode_option_in_ui().await
|
||||||
});
|
});
|
||||||
@@ -122,7 +124,7 @@ pub unsafe extern "C" fn amfi_enable_developer_mode(
|
|||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.enable_developer_mode().await
|
client_ref.enable_developer_mode().await
|
||||||
});
|
});
|
||||||
@@ -150,7 +152,7 @@ pub unsafe extern "C" fn amfi_accept_developer_mode(
|
|||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.accept_developer_mode().await
|
client_ref.accept_developer_mode().await
|
||||||
});
|
});
|
||||||
@@ -171,7 +173,7 @@ pub unsafe extern "C" fn amfi_accept_developer_mode(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn amfi_client_free(handle: *mut AmfiClientHandle) {
|
pub unsafe extern "C" fn amfi_client_free(handle: *mut AmfiClientHandle) {
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
log::debug!("Freeing AmfiClient handle");
|
tracing::debug!("Freeing AmfiClient handle");
|
||||||
let _ = unsafe { Box::from_raw(handle) };
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use idevice::{IdeviceError, ReadWrite, RsdService};
|
|||||||
|
|
||||||
use crate::core_device_proxy::AdapterHandle;
|
use crate::core_device_proxy::AdapterHandle;
|
||||||
use crate::rsd::RsdHandshakeHandle;
|
use crate::rsd::RsdHandshakeHandle;
|
||||||
use crate::{IdeviceFfiError, RUNTIME, ReadWriteOpaque, ffi_err};
|
use crate::{IdeviceFfiError, ReadWriteOpaque, ffi_err, run_sync, run_sync_local};
|
||||||
|
|
||||||
/// Opaque handle to an AppServiceClient
|
/// Opaque handle to an AppServiceClient
|
||||||
pub struct AppServiceHandle(pub AppServiceClient<Box<dyn ReadWrite>>);
|
pub struct AppServiceHandle(pub AppServiceClient<Box<dyn ReadWrite>>);
|
||||||
@@ -91,7 +91,7 @@ pub unsafe extern "C" fn app_service_connect_rsd(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<AppServiceClient<Box<dyn ReadWrite>>, IdeviceError> =
|
let res: Result<AppServiceClient<Box<dyn ReadWrite>>, IdeviceError> =
|
||||||
RUNTIME.block_on(async move {
|
run_sync_local(async move {
|
||||||
let provider_ref = unsafe { &mut (*provider).0 };
|
let provider_ref = unsafe { &mut (*provider).0 };
|
||||||
let handshake_ref = unsafe { &mut (*handshake).0 };
|
let handshake_ref = unsafe { &mut (*handshake).0 };
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ pub unsafe extern "C" fn app_service_new(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let socket = unsafe { Box::from_raw(socket) };
|
let socket = unsafe { Box::from_raw(socket) };
|
||||||
let res = RUNTIME.block_on(async move { AppServiceClient::new(socket.inner.unwrap()).await });
|
let res = run_sync(async move { AppServiceClient::new(socket.inner.unwrap()).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(client) => {
|
Ok(client) => {
|
||||||
@@ -186,7 +186,7 @@ pub unsafe extern "C" fn app_service_list_apps(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move {
|
let res = run_sync(async move {
|
||||||
client
|
client
|
||||||
.list_apps(
|
.list_apps(
|
||||||
app_clips != 0,
|
app_clips != 0,
|
||||||
@@ -347,7 +347,7 @@ pub unsafe extern "C" fn app_service_launch_app(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move {
|
let res = run_sync(async move {
|
||||||
client
|
client
|
||||||
.launch_application(
|
.launch_application(
|
||||||
bundle_id_str,
|
bundle_id_str,
|
||||||
@@ -430,7 +430,7 @@ pub unsafe extern "C" fn app_service_list_processes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move { client.list_processes().await });
|
let res = run_sync(async move { client.list_processes().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(process_list) => {
|
Ok(process_list) => {
|
||||||
@@ -477,7 +477,7 @@ pub unsafe extern "C" fn app_service_free_process_list(
|
|||||||
count: usize,
|
count: usize,
|
||||||
) {
|
) {
|
||||||
if !processes.is_null() && count > 0 {
|
if !processes.is_null() && count > 0 {
|
||||||
let processes_slice = unsafe { std::slice::from_raw_parts_mut(processes, count) };
|
let processes_slice = unsafe { Vec::from_raw_parts(processes, count, count) };
|
||||||
for process in processes_slice {
|
for process in processes_slice {
|
||||||
if !process.executable_url.is_null() {
|
if !process.executable_url.is_null() {
|
||||||
let _ = unsafe { CString::from_raw(process.executable_url) };
|
let _ = unsafe { CString::from_raw(process.executable_url) };
|
||||||
@@ -513,7 +513,7 @@ pub unsafe extern "C" fn app_service_uninstall_app(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move { client.uninstall_app(bundle_id_str).await });
|
let res = run_sync(async move { client.uninstall_app(bundle_id_str).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
@@ -546,7 +546,7 @@ pub unsafe extern "C" fn app_service_send_signal(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move { client.send_signal(pid, signal).await });
|
let res = run_sync(async move { client.send_signal(pid, signal).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(signal_response) => {
|
Ok(signal_response) => {
|
||||||
@@ -628,7 +628,7 @@ pub unsafe extern "C" fn app_service_fetch_app_icon(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move {
|
let res = run_sync(async move {
|
||||||
client
|
client
|
||||||
.fetch_app_icon(bundle_id_str, width, height, scale, allow_placeholder != 0)
|
.fetch_app_icon(bundle_id_str, width, height, scale, allow_placeholder != 0)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ use std::ptr::null_mut;
|
|||||||
use futures::{Stream, StreamExt};
|
use futures::{Stream, StreamExt};
|
||||||
use idevice::core_device::DiagnostisServiceClient;
|
use idevice::core_device::DiagnostisServiceClient;
|
||||||
use idevice::{IdeviceError, ReadWrite, RsdService};
|
use idevice::{IdeviceError, ReadWrite, RsdService};
|
||||||
use log::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::core_device_proxy::AdapterHandle;
|
use crate::core_device_proxy::AdapterHandle;
|
||||||
use crate::rsd::RsdHandshakeHandle;
|
use crate::rsd::RsdHandshakeHandle;
|
||||||
use crate::{IdeviceFfiError, RUNTIME, ReadWriteOpaque, ffi_err};
|
use crate::{IdeviceFfiError, ReadWriteOpaque, ffi_err, run_sync, run_sync_local};
|
||||||
|
|
||||||
/// Opaque handle to an AppServiceClient
|
/// Opaque handle to an AppServiceClient
|
||||||
pub struct DiagnosticsServiceHandle(pub DiagnostisServiceClient<Box<dyn ReadWrite>>);
|
pub struct DiagnosticsServiceHandle(pub DiagnostisServiceClient<Box<dyn ReadWrite>>);
|
||||||
@@ -43,7 +43,7 @@ pub unsafe extern "C" fn diagnostics_service_connect_rsd(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<DiagnostisServiceClient<Box<dyn ReadWrite>>, IdeviceError> =
|
let res: Result<DiagnostisServiceClient<Box<dyn ReadWrite>>, IdeviceError> =
|
||||||
RUNTIME.block_on(async move {
|
run_sync_local(async move {
|
||||||
let provider_ref = unsafe { &mut (*provider).0 };
|
let provider_ref = unsafe { &mut (*provider).0 };
|
||||||
let handshake_ref = unsafe { &mut (*handshake).0 };
|
let handshake_ref = unsafe { &mut (*handshake).0 };
|
||||||
debug!(
|
debug!(
|
||||||
@@ -87,8 +87,8 @@ pub unsafe extern "C" fn diagnostics_service_new(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let socket = unsafe { Box::from_raw(socket) };
|
let socket = unsafe { Box::from_raw(socket) };
|
||||||
let res = RUNTIME
|
let res =
|
||||||
.block_on(async move { DiagnostisServiceClient::from_stream(socket.inner.unwrap()).await });
|
run_sync(async move { DiagnostisServiceClient::from_stream(socket.inner.unwrap()).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(client) => {
|
Ok(client) => {
|
||||||
@@ -134,7 +134,7 @@ pub unsafe extern "C" fn diagnostics_service_capture_sysdiagnose(
|
|||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
let handle = unsafe { &mut *handle };
|
let handle = unsafe { &mut *handle };
|
||||||
let res = RUNTIME.block_on(async move { handle.0.capture_sysdiagnose(dry_run).await });
|
let res = run_sync_local(async move { handle.0.capture_sysdiagnose(dry_run).await });
|
||||||
match res {
|
match res {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
let filename = CString::new(res.preferred_filename).unwrap();
|
let filename = CString::new(res.preferred_filename).unwrap();
|
||||||
@@ -172,7 +172,7 @@ pub unsafe extern "C" fn sysdiagnose_stream_next(
|
|||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
let handle = unsafe { &mut *handle };
|
let handle = unsafe { &mut *handle };
|
||||||
let res = RUNTIME.block_on(async move { handle.0.next().await });
|
let res = run_sync_local(async move { handle.0.next().await });
|
||||||
match res {
|
match res {
|
||||||
Some(Ok(res)) => {
|
Some(Ok(res)) => {
|
||||||
let mut res = res.into_boxed_slice();
|
let mut res = res.into_boxed_slice();
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ use idevice::{
|
|||||||
IdeviceError, IdeviceService, core_device_proxy::CoreDeviceProxy, provider::IdeviceProvider,
|
IdeviceError, IdeviceService, core_device_proxy::CoreDeviceProxy, provider::IdeviceProvider,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, IdeviceHandle, RUNTIME, ffi_err, provider::IdeviceProviderHandle};
|
use crate::{
|
||||||
|
IdeviceFfiError, IdeviceHandle, ffi_err, provider::IdeviceProviderHandle, run_sync,
|
||||||
|
run_sync_local,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct CoreDeviceProxyHandle(pub CoreDeviceProxy);
|
pub struct CoreDeviceProxyHandle(pub CoreDeviceProxy);
|
||||||
pub struct AdapterHandle(pub idevice::tcp::handle::AdapterHandle);
|
pub struct AdapterHandle(pub idevice::tcp::handle::AdapterHandle);
|
||||||
@@ -32,11 +35,11 @@ pub unsafe extern "C" fn core_device_proxy_connect(
|
|||||||
client: *mut *mut CoreDeviceProxyHandle,
|
client: *mut *mut CoreDeviceProxyHandle,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if provider.is_null() || client.is_null() {
|
if provider.is_null() || client.is_null() {
|
||||||
log::error!("Null pointer provided");
|
tracing::error!("Null pointer provided");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<CoreDeviceProxy, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<CoreDeviceProxy, IdeviceError> = run_sync_local(async move {
|
||||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
|
|
||||||
// Connect using the reference
|
// Connect using the reference
|
||||||
@@ -76,7 +79,7 @@ pub unsafe extern "C" fn core_device_proxy_new(
|
|||||||
}
|
}
|
||||||
let socket = unsafe { Box::from_raw(socket) }.0;
|
let socket = unsafe { Box::from_raw(socket) }.0;
|
||||||
let r: Result<CoreDeviceProxy, IdeviceError> =
|
let r: Result<CoreDeviceProxy, IdeviceError> =
|
||||||
RUNTIME.block_on(async move { CoreDeviceProxy::new(socket).await });
|
run_sync(async move { CoreDeviceProxy::new(socket).await });
|
||||||
match r {
|
match r {
|
||||||
Ok(r) => {
|
Ok(r) => {
|
||||||
let boxed = Box::new(CoreDeviceProxyHandle(r));
|
let boxed = Box::new(CoreDeviceProxyHandle(r));
|
||||||
@@ -113,7 +116,7 @@ pub unsafe extern "C" fn core_device_proxy_send(
|
|||||||
let proxy = unsafe { &mut (*handle).0 };
|
let proxy = unsafe { &mut (*handle).0 };
|
||||||
let data_slice = unsafe { std::slice::from_raw_parts(data, length) };
|
let data_slice = unsafe { std::slice::from_raw_parts(data, length) };
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async move { proxy.send(data_slice).await });
|
let res = run_sync(async move { proxy.send(data_slice).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
@@ -149,7 +152,7 @@ pub unsafe extern "C" fn core_device_proxy_recv(
|
|||||||
|
|
||||||
let proxy = unsafe { &mut (*handle).0 };
|
let proxy = unsafe { &mut (*handle).0 };
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async move { proxy.recv().await });
|
let res = run_sync(async move { proxy.recv().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(received_data) => {
|
Ok(received_data) => {
|
||||||
@@ -192,7 +195,7 @@ pub unsafe extern "C" fn core_device_proxy_get_client_parameters(
|
|||||||
netmask: *mut *mut c_char,
|
netmask: *mut *mut c_char,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if handle.is_null() {
|
if handle.is_null() {
|
||||||
log::error!("Passed null handle");
|
tracing::error!("Passed null handle");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +315,7 @@ pub unsafe extern "C" fn core_device_proxy_create_tcp_adapter(
|
|||||||
match result {
|
match result {
|
||||||
Ok(adapter_obj) => {
|
Ok(adapter_obj) => {
|
||||||
// We have to run this in the RUNTIME since we're spawning a new thread
|
// We have to run this in the RUNTIME since we're spawning a new thread
|
||||||
let adapter_handle = RUNTIME.block_on(async move { adapter_obj.to_async_handle() });
|
let adapter_handle = run_sync(async move { adapter_obj.to_async_handle() });
|
||||||
|
|
||||||
let boxed = Box::new(AdapterHandle(adapter_handle));
|
let boxed = Box::new(AdapterHandle(adapter_handle));
|
||||||
unsafe { *adapter = Box::into_raw(boxed) };
|
unsafe { *adapter = Box::into_raw(boxed) };
|
||||||
@@ -333,7 +336,7 @@ pub unsafe extern "C" fn core_device_proxy_create_tcp_adapter(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn core_device_proxy_free(handle: *mut CoreDeviceProxyHandle) {
|
pub unsafe extern "C" fn core_device_proxy_free(handle: *mut CoreDeviceProxyHandle) {
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
log::debug!("Freeing core_device_proxy");
|
tracing::debug!("Freeing core_device_proxy");
|
||||||
let _ = unsafe { Box::from_raw(handle) };
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,7 +352,7 @@ pub unsafe extern "C" fn core_device_proxy_free(handle: *mut CoreDeviceProxyHand
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn adapter_free(handle: *mut AdapterHandle) {
|
pub unsafe extern "C" fn adapter_free(handle: *mut AdapterHandle) {
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
log::debug!("Freeing adapter");
|
tracing::debug!("Freeing adapter");
|
||||||
let _ = unsafe { Box::from_raw(handle) };
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use idevice::{IdeviceError, ReadWrite, RsdService};
|
|||||||
|
|
||||||
use crate::core_device_proxy::AdapterHandle;
|
use crate::core_device_proxy::AdapterHandle;
|
||||||
use crate::rsd::RsdHandshakeHandle;
|
use crate::rsd::RsdHandshakeHandle;
|
||||||
use crate::{IdeviceFfiError, RUNTIME, ReadWriteOpaque, ffi_err};
|
use crate::{IdeviceFfiError, ReadWriteOpaque, ffi_err, run_sync, run_sync_local};
|
||||||
|
|
||||||
/// Opaque handle to a DebugProxyClient
|
/// Opaque handle to a DebugProxyClient
|
||||||
pub struct DebugProxyHandle(pub DebugProxyClient<Box<dyn ReadWrite>>);
|
pub struct DebugProxyHandle(pub DebugProxyClient<Box<dyn ReadWrite>>);
|
||||||
@@ -136,7 +136,7 @@ pub unsafe extern "C" fn debug_proxy_connect_rsd(
|
|||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
let res: Result<DebugProxyClient<Box<dyn ReadWrite>>, IdeviceError> =
|
let res: Result<DebugProxyClient<Box<dyn ReadWrite>>, IdeviceError> =
|
||||||
RUNTIME.block_on(async move {
|
run_sync_local(async move {
|
||||||
let provider_ref = unsafe { &mut (*provider).0 };
|
let provider_ref = unsafe { &mut (*provider).0 };
|
||||||
let handshake_ref = unsafe { &mut (*handshake).0 };
|
let handshake_ref = unsafe { &mut (*handshake).0 };
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@ pub unsafe extern "C" fn debug_proxy_send_command(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async move { client.send_command(cmd).await });
|
let res = run_sync(async move { client.send_command(cmd).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(Some(r)) => {
|
Ok(Some(r)) => {
|
||||||
@@ -282,7 +282,7 @@ pub unsafe extern "C" fn debug_proxy_read_response(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move { client.read_response().await });
|
let res = run_sync(async move { client.read_response().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(Some(r)) => {
|
Ok(Some(r)) => {
|
||||||
@@ -326,7 +326,7 @@ pub unsafe extern "C" fn debug_proxy_send_raw(
|
|||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let data_slice = unsafe { std::slice::from_raw_parts(data, len) };
|
let data_slice = unsafe { std::slice::from_raw_parts(data, len) };
|
||||||
let res = RUNTIME.block_on(async move { client.send_raw(data_slice).await });
|
let res = run_sync(async move { client.send_raw(data_slice).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
@@ -358,7 +358,7 @@ pub unsafe extern "C" fn debug_proxy_read(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move { client.read(len).await });
|
let res = run_sync(async move { client.read(len).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(r) => {
|
Ok(r) => {
|
||||||
@@ -416,7 +416,7 @@ pub unsafe extern "C" fn debug_proxy_set_argv(
|
|||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async move { client.set_argv(argv_vec).await });
|
let res = run_sync(async move { client.set_argv(argv_vec).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(r) => {
|
Ok(r) => {
|
||||||
@@ -450,7 +450,7 @@ pub unsafe extern "C" fn debug_proxy_send_ack(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move { client.send_ack().await });
|
let res = run_sync(async move { client.send_ack().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
@@ -477,7 +477,7 @@ pub unsafe extern "C" fn debug_proxy_send_nack(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move { client.send_noack().await });
|
let res = run_sync(async move { client.send_noack().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
|
|||||||
533
ffi/src/diagnostics_relay.rs
Normal file
533
ffi/src/diagnostics_relay.rs
Normal file
@@ -0,0 +1,533 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
ffi::{CStr, c_char},
|
||||||
|
ptr::null_mut,
|
||||||
|
};
|
||||||
|
|
||||||
|
use idevice::{
|
||||||
|
IdeviceError, IdeviceService, diagnostics_relay::DiagnosticsRelayClient,
|
||||||
|
provider::IdeviceProvider,
|
||||||
|
};
|
||||||
|
use plist_ffi::plist_t;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
IdeviceFfiError, IdeviceHandle, ffi_err, provider::IdeviceProviderHandle, run_sync_local,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct DiagnosticsRelayClientHandle(pub DiagnosticsRelayClient);
|
||||||
|
|
||||||
|
/// Automatically creates and connects to Diagnostics Relay, returning a client handle
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`provider`] - An IdeviceProvider
|
||||||
|
/// * [`client`] - On success, will be set to point to a newly allocated DiagnosticsRelayClient handle
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `provider` must be a valid pointer to a handle allocated by this library
|
||||||
|
/// `client` must be a valid, non-null pointer to a location where the handle will be stored
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn diagnostics_relay_client_connect(
|
||||||
|
provider: *mut IdeviceProviderHandle,
|
||||||
|
client: *mut *mut DiagnosticsRelayClientHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if provider.is_null() || client.is_null() {
|
||||||
|
tracing::error!("Null pointer provided");
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res: Result<DiagnosticsRelayClient, IdeviceError> = run_sync_local(async move {
|
||||||
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
|
// Connect using the reference
|
||||||
|
DiagnosticsRelayClient::connect(provider_ref).await
|
||||||
|
});
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(r) => {
|
||||||
|
let boxed = Box::new(DiagnosticsRelayClientHandle(r));
|
||||||
|
unsafe { *client = Box::into_raw(boxed) };
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
ffi_err!(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Automatically creates and connects to Diagnostics Relay, returning a client handle
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`socket`] - An IdeviceSocket handle
|
||||||
|
/// * [`client`] - On success, will be set to point to a newly allocated DiagnosticsRelayClient handle
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `socket` must be a valid pointer to a handle allocated by this library. The socket is consumed,
|
||||||
|
/// and may not be used again.
|
||||||
|
/// `client` must be a valid, non-null pointer to a location where the handle will be stored
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn diagnostics_relay_client_new(
|
||||||
|
socket: *mut IdeviceHandle,
|
||||||
|
client: *mut *mut DiagnosticsRelayClientHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if socket.is_null() || client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
let socket = unsafe { Box::from_raw(socket) }.0;
|
||||||
|
let r = DiagnosticsRelayClient::new(socket);
|
||||||
|
let boxed = Box::new(DiagnosticsRelayClientHandle(r));
|
||||||
|
unsafe { *client = Box::into_raw(boxed) };
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Queries the device IO registry
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid DiagnosticsRelayClient handle
|
||||||
|
/// * `current_plane` - A string to search by or null
|
||||||
|
/// * `entry_name` - A string to search by or null
|
||||||
|
/// * `entry_class` - A string to search by or null
|
||||||
|
/// * `res` - Will be set to a pointer of a plist dictionary node on search success
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success. Note that res can be null on success
|
||||||
|
/// if the search resulted in no values.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn diagnostics_relay_client_ioregistry(
|
||||||
|
client: *mut DiagnosticsRelayClientHandle,
|
||||||
|
current_plane: *const c_char,
|
||||||
|
entry_name: *const c_char,
|
||||||
|
entry_class: *const c_char,
|
||||||
|
res: *mut plist_t,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
let current_plane = if current_plane.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(match unsafe { CStr::from_ptr(current_plane) }.to_str() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidString);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let entry_name = if entry_name.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(match unsafe { CStr::from_ptr(entry_name) }.to_str() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidString);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let entry_class = if entry_class.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(match unsafe { CStr::from_ptr(entry_class) }.to_str() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidString);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let output: Result<Option<plist::Dictionary>, IdeviceError> = run_sync_local(async move {
|
||||||
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
|
client_ref
|
||||||
|
.ioregistry(current_plane, entry_name, entry_class)
|
||||||
|
.await
|
||||||
|
});
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(output) => {
|
||||||
|
let output = match output {
|
||||||
|
Some(res) => {
|
||||||
|
plist_ffi::PlistWrapper::new_node(plist::Value::Dictionary(res)).into_ptr()
|
||||||
|
}
|
||||||
|
None => null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { *res = output }
|
||||||
|
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests MobileGestalt information from the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid DiagnosticsRelayClient handle
|
||||||
|
/// * `keys` - Optional list of specific keys to request. If None, requests all available keys
|
||||||
|
/// * `res` - Will be set to a pointer of a plist dictionary node on search success
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success. Note that res can be null on success
|
||||||
|
/// if the search resulted in no values.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn diagnostics_relay_client_mobilegestalt(
|
||||||
|
client: *mut DiagnosticsRelayClientHandle,
|
||||||
|
keys: *const *const c_char,
|
||||||
|
keys_len: usize,
|
||||||
|
res: *mut plist_t,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let keys = if !keys.is_null() {
|
||||||
|
let keys = unsafe { std::slice::from_raw_parts(keys, keys_len) };
|
||||||
|
Some(
|
||||||
|
keys.iter()
|
||||||
|
.filter_map(|x| unsafe {
|
||||||
|
match CStr::from_ptr(*x).to_str() {
|
||||||
|
Ok(s) => Some(s.to_string()),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let output: Result<Option<plist::Dictionary>, IdeviceError> = run_sync_local(async move {
|
||||||
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
|
client_ref.mobilegestalt(keys).await
|
||||||
|
});
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(output) => {
|
||||||
|
let output = match output {
|
||||||
|
Some(res) => {
|
||||||
|
plist_ffi::PlistWrapper::new_node(plist::Value::Dictionary(res)).into_ptr()
|
||||||
|
}
|
||||||
|
None => null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { *res = output }
|
||||||
|
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests gas gauge information from the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid DiagnosticsRelayClient handle
|
||||||
|
/// * `res` - Will be set to a pointer of a plist dictionary node on search success
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success. Note that res can be null on success
|
||||||
|
/// if the search resulted in no values.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn diagnostics_relay_client_gasguage(
|
||||||
|
client: *mut DiagnosticsRelayClientHandle,
|
||||||
|
res: *mut plist_t,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output: Result<Option<plist::Dictionary>, IdeviceError> = run_sync_local(async move {
|
||||||
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
|
client_ref.gasguage().await
|
||||||
|
});
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(output) => {
|
||||||
|
let output = match output {
|
||||||
|
Some(res) => {
|
||||||
|
plist_ffi::PlistWrapper::new_node(plist::Value::Dictionary(res)).into_ptr()
|
||||||
|
}
|
||||||
|
None => null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { *res = output }
|
||||||
|
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests nand information from the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid DiagnosticsRelayClient handle
|
||||||
|
/// * `res` - Will be set to a pointer of a plist dictionary node on search success
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success. Note that res can be null on success
|
||||||
|
/// if the search resulted in no values.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn diagnostics_relay_client_nand(
|
||||||
|
client: *mut DiagnosticsRelayClientHandle,
|
||||||
|
res: *mut plist_t,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output: Result<Option<plist::Dictionary>, IdeviceError> = run_sync_local(async move {
|
||||||
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
|
client_ref.nand().await
|
||||||
|
});
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(output) => {
|
||||||
|
let output = match output {
|
||||||
|
Some(res) => {
|
||||||
|
plist_ffi::PlistWrapper::new_node(plist::Value::Dictionary(res)).into_ptr()
|
||||||
|
}
|
||||||
|
None => null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { *res = output }
|
||||||
|
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests all available information from the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid DiagnosticsRelayClient handle
|
||||||
|
/// * `res` - Will be set to a pointer of a plist dictionary node on search success
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success. Note that res can be null on success
|
||||||
|
/// if the search resulted in no values.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn diagnostics_relay_client_all(
|
||||||
|
client: *mut DiagnosticsRelayClientHandle,
|
||||||
|
res: *mut plist_t,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output: Result<Option<plist::Dictionary>, IdeviceError> = run_sync_local(async move {
|
||||||
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
|
client_ref.all().await
|
||||||
|
});
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(output) => {
|
||||||
|
let output = match output {
|
||||||
|
Some(res) => {
|
||||||
|
plist_ffi::PlistWrapper::new_node(plist::Value::Dictionary(res)).into_ptr()
|
||||||
|
}
|
||||||
|
None => null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { *res = output }
|
||||||
|
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Restarts the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid DiagnosticsRelayClient handle
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn diagnostics_relay_client_restart(
|
||||||
|
client: *mut DiagnosticsRelayClientHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
|
client_ref.restart().await
|
||||||
|
});
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(_) => null_mut(),
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shuts down the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid DiagnosticsRelayClient handle
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn diagnostics_relay_client_shutdown(
|
||||||
|
client: *mut DiagnosticsRelayClientHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
|
client_ref.shutdown().await
|
||||||
|
});
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(_) => null_mut(),
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Puts the device to sleep
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid DiagnosticsRelayClient handle
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn diagnostics_relay_client_sleep(
|
||||||
|
client: *mut DiagnosticsRelayClientHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
|
client_ref.sleep().await
|
||||||
|
});
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(_) => null_mut(),
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests WiFi diagnostics from the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid DiagnosticsRelayClient handle
|
||||||
|
/// * `res` - Will be set to a pointer of a plist dictionary node on search success
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success. Note that res can be null on success
|
||||||
|
/// if the search resulted in no values.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn diagnostics_relay_client_wifi(
|
||||||
|
client: *mut DiagnosticsRelayClientHandle,
|
||||||
|
res: *mut plist_t,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output: Result<Option<plist::Dictionary>, IdeviceError> = run_sync_local(async move {
|
||||||
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
|
client_ref.wifi().await
|
||||||
|
});
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(output) => {
|
||||||
|
let output = match output {
|
||||||
|
Some(res) => {
|
||||||
|
plist_ffi::PlistWrapper::new_node(plist::Value::Dictionary(res)).into_ptr()
|
||||||
|
}
|
||||||
|
None => null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { *res = output }
|
||||||
|
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Puts the device to sleep
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid DiagnosticsRelayClient handle
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn diagnostics_relay_client_goodbye(
|
||||||
|
client: *mut DiagnosticsRelayClientHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
|
client_ref.goodbye().await
|
||||||
|
});
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(_) => null_mut(),
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frees a handle
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`handle`] - The handle to free
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `handle` must be a valid pointer to the handle that was allocated by this library,
|
||||||
|
/// or NULL (in which case this function does nothing)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn diagnostics_relay_client_free(handle: *mut DiagnosticsRelayClientHandle) {
|
||||||
|
if !handle.is_null() {
|
||||||
|
tracing::debug!("Freeing DiagnosticsRelayClientHandle");
|
||||||
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ use std::ptr::null_mut;
|
|||||||
|
|
||||||
use idevice::{ReadWrite, dvt::location_simulation::LocationSimulationClient};
|
use idevice::{ReadWrite, dvt::location_simulation::LocationSimulationClient};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, RUNTIME, ffi_err, remote_server::RemoteServerHandle};
|
use crate::{IdeviceFfiError, dvt::remote_server::RemoteServerHandle, ffi_err, run_sync};
|
||||||
|
|
||||||
/// Opaque handle to a ProcessControlClient
|
/// Opaque handle to a ProcessControlClient
|
||||||
pub struct LocationSimulationHandle<'a>(pub LocationSimulationClient<'a, Box<dyn ReadWrite>>);
|
pub struct LocationSimulationHandle<'a>(pub LocationSimulationClient<'a, Box<dyn ReadWrite>>);
|
||||||
@@ -31,7 +31,7 @@ pub unsafe extern "C" fn location_simulation_new(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let server = unsafe { &mut (*server).0 };
|
let server = unsafe { &mut (*server).0 };
|
||||||
let res = RUNTIME.block_on(async move { LocationSimulationClient::new(server).await });
|
let res = run_sync(async move { LocationSimulationClient::new(server).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(client) => {
|
Ok(client) => {
|
||||||
@@ -76,7 +76,7 @@ pub unsafe extern "C" fn location_simulation_clear(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move { client.clear().await });
|
let res = run_sync(async move { client.clear().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
@@ -107,7 +107,7 @@ pub unsafe extern "C" fn location_simulation_set(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move { client.set(latitude, longitude).await });
|
let res = run_sync(async move { client.set(latitude, longitude).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
8
ffi/src/dvt/mod.rs
Normal file
8
ffi/src/dvt/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#[cfg(feature = "location_simulation")]
|
||||||
|
pub mod location_simulation;
|
||||||
|
|
||||||
|
pub mod process_control;
|
||||||
|
pub mod remote_server;
|
||||||
|
pub mod screenshot;
|
||||||
@@ -8,7 +8,7 @@ use std::{
|
|||||||
use idevice::{ReadWrite, dvt::process_control::ProcessControlClient};
|
use idevice::{ReadWrite, dvt::process_control::ProcessControlClient};
|
||||||
use plist::{Dictionary, Value};
|
use plist::{Dictionary, Value};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, RUNTIME, ffi_err, remote_server::RemoteServerHandle};
|
use crate::{IdeviceFfiError, dvt::remote_server::RemoteServerHandle, ffi_err, run_sync};
|
||||||
|
|
||||||
/// Opaque handle to a ProcessControlClient
|
/// Opaque handle to a ProcessControlClient
|
||||||
pub struct ProcessControlHandle<'a>(pub ProcessControlClient<'a, Box<dyn ReadWrite>>);
|
pub struct ProcessControlHandle<'a>(pub ProcessControlClient<'a, Box<dyn ReadWrite>>);
|
||||||
@@ -35,7 +35,7 @@ pub unsafe extern "C" fn process_control_new(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let server = unsafe { &mut (*server).0 };
|
let server = unsafe { &mut (*server).0 };
|
||||||
let res = RUNTIME.block_on(async move { ProcessControlClient::new(server).await });
|
let res = run_sync(async move { ProcessControlClient::new(server).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(client) => {
|
Ok(client) => {
|
||||||
@@ -128,7 +128,7 @@ pub unsafe extern "C" fn process_control_launch_app(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move {
|
let res = run_sync(async move {
|
||||||
client
|
client
|
||||||
.launch_app(
|
.launch_app(
|
||||||
bundle_id,
|
bundle_id,
|
||||||
@@ -170,7 +170,7 @@ pub unsafe extern "C" fn process_control_kill_app(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move { client.kill_app(pid).await });
|
let res = run_sync(async move { client.kill_app(pid).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
@@ -199,7 +199,7 @@ pub unsafe extern "C" fn process_control_disable_memory_limit(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let client = unsafe { &mut (*handle).0 };
|
let client = unsafe { &mut (*handle).0 };
|
||||||
let res = RUNTIME.block_on(async move { client.disable_memory_limit(pid).await });
|
let res = run_sync(async move { client.disable_memory_limit(pid).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
@@ -4,7 +4,7 @@ use std::ptr::null_mut;
|
|||||||
|
|
||||||
use crate::core_device_proxy::AdapterHandle;
|
use crate::core_device_proxy::AdapterHandle;
|
||||||
use crate::rsd::RsdHandshakeHandle;
|
use crate::rsd::RsdHandshakeHandle;
|
||||||
use crate::{IdeviceFfiError, RUNTIME, ReadWriteOpaque, ffi_err};
|
use crate::{IdeviceFfiError, ReadWriteOpaque, ffi_err, run_sync, run_sync_local};
|
||||||
use idevice::dvt::remote_server::RemoteServerClient;
|
use idevice::dvt::remote_server::RemoteServerClient;
|
||||||
use idevice::{IdeviceError, ReadWrite, RsdService};
|
use idevice::{IdeviceError, ReadWrite, RsdService};
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ pub unsafe extern "C" fn remote_server_new(
|
|||||||
|
|
||||||
let res: Result<RemoteServerClient<Box<dyn ReadWrite>>, IdeviceError> =
|
let res: Result<RemoteServerClient<Box<dyn ReadWrite>>, IdeviceError> =
|
||||||
match wrapper.inner.take() {
|
match wrapper.inner.take() {
|
||||||
Some(stream) => RUNTIME.block_on(async move {
|
Some(stream) => run_sync(async move {
|
||||||
let mut client = RemoteServerClient::new(stream);
|
let mut client = RemoteServerClient::new(stream);
|
||||||
client.read_message(0).await?;
|
client.read_message(0).await?;
|
||||||
Ok(client)
|
Ok(client)
|
||||||
@@ -77,7 +77,7 @@ pub unsafe extern "C" fn remote_server_connect_rsd(
|
|||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
let res: Result<RemoteServerClient<Box<dyn ReadWrite>>, IdeviceError> =
|
let res: Result<RemoteServerClient<Box<dyn ReadWrite>>, IdeviceError> =
|
||||||
RUNTIME.block_on(async move {
|
run_sync_local(async move {
|
||||||
let provider_ref = unsafe { &mut (*provider).0 };
|
let provider_ref = unsafe { &mut (*provider).0 };
|
||||||
let handshake_ref = unsafe { &mut (*handshake).0 };
|
let handshake_ref = unsafe { &mut (*handshake).0 };
|
||||||
|
|
||||||
113
ffi/src/dvt/screenshot.rs
Normal file
113
ffi/src/dvt/screenshot.rs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
|
||||||
|
use idevice::{ReadWrite, dvt::screenshot::ScreenshotClient};
|
||||||
|
|
||||||
|
use crate::{IdeviceFfiError, dvt::remote_server::RemoteServerHandle, ffi_err, run_sync};
|
||||||
|
|
||||||
|
/// An opaque FFI handle for a [`ScreenshotClient`].
|
||||||
|
///
|
||||||
|
/// This type wraps a [`ScreenshotClient`] that communicates with
|
||||||
|
/// a connected device to capture screenshots through the DVT (Device Virtualization Toolkit) service.
|
||||||
|
pub struct ScreenshotClientHandle<'a>(pub ScreenshotClient<'a, Box<dyn ReadWrite>>);
|
||||||
|
|
||||||
|
/// Creates a new [`ScreenshotClient`] associated with a given [`RemoteServerHandle`].
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `server` - A pointer to a valid [`RemoteServerHandle`], previously created by this library.
|
||||||
|
/// * `handle` - A pointer to a location where the newly created [`ScreenshotClientHandle`] will be stored.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * `null_mut()` on success.
|
||||||
|
/// * A pointer to an [`IdeviceFfiError`] on failure.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// - `server` must be a non-null pointer to a valid remote server handle allocated by this library.
|
||||||
|
/// - `handle` must be a non-null pointer to a writable memory location where the handle will be stored.
|
||||||
|
/// - The returned handle must later be freed using [`screenshot_client_free`].
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn screenshot_client_new(
|
||||||
|
server: *mut RemoteServerHandle,
|
||||||
|
handle: *mut *mut ScreenshotClientHandle<'static>,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if server.is_null() || handle.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let server = unsafe { &mut (*server).0 };
|
||||||
|
let res = run_sync(async move { ScreenshotClient::new(server).await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(client) => {
|
||||||
|
let boxed = Box::new(ScreenshotClientHandle(client));
|
||||||
|
unsafe { *handle = Box::into_raw(boxed) };
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frees a [`ScreenshotClientHandle`].
|
||||||
|
///
|
||||||
|
/// This releases all memory associated with the handle.
|
||||||
|
/// After calling this function, the handle pointer must not be used again.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `handle` - Pointer to a [`ScreenshotClientHandle`] previously returned by [`screenshot_client_new`].
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// - `handle` must either be `NULL` or a valid pointer created by this library.
|
||||||
|
/// - Double-freeing or using the handle after freeing causes undefined behavior.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn screenshot_client_free(handle: *mut ScreenshotClientHandle<'static>) {
|
||||||
|
if !handle.is_null() {
|
||||||
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Captures a screenshot from the connected device.
|
||||||
|
///
|
||||||
|
/// On success, this function writes a pointer to the PNG-encoded screenshot data and its length
|
||||||
|
/// into the provided output arguments. The caller is responsible for freeing this data using
|
||||||
|
/// `idevice_data_free`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `handle` - A pointer to a valid [`ScreenshotClientHandle`].
|
||||||
|
/// * `data` - Output pointer where the screenshot buffer pointer will be written.
|
||||||
|
/// * `len` - Output pointer where the buffer length (in bytes) will be written.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * `null_mut()` on success.
|
||||||
|
/// * A pointer to an [`IdeviceFfiError`] on failure.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// - `handle` must be a valid pointer to a [`ScreenshotClientHandle`].
|
||||||
|
/// - `data` and `len` must be valid writable pointers.
|
||||||
|
/// - The data returned through `*data` must be freed by the caller with `idevice_data_free`.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn screenshot_client_take_screenshot(
|
||||||
|
handle: *mut ScreenshotClientHandle<'static>,
|
||||||
|
data: *mut *mut u8,
|
||||||
|
len: *mut usize,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if handle.is_null() || data.is_null() || len.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let client = unsafe { &mut (*handle).0 };
|
||||||
|
let res = run_sync(async move { client.take_screenshot().await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(r) => {
|
||||||
|
let mut r = r.into_boxed_slice();
|
||||||
|
unsafe {
|
||||||
|
*data = r.as_mut_ptr();
|
||||||
|
*len = r.len();
|
||||||
|
}
|
||||||
|
std::mem::forget(r); // Prevent Rust from freeing the buffer
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,7 +35,7 @@ macro_rules! ffi_err {
|
|||||||
|
|
||||||
let err: IdeviceError = $err.into();
|
let err: IdeviceError = $err.into();
|
||||||
let code = err.code();
|
let code = err.code();
|
||||||
let msg = CString::new(err.to_string())
|
let msg = CString::new(format!("{:?}", err))
|
||||||
.unwrap_or_else(|_| CString::new("invalid error").unwrap());
|
.unwrap_or_else(|_| CString::new("invalid error").unwrap());
|
||||||
let raw_msg = msg.into_raw();
|
let raw_msg = msg.into_raw();
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ use idevice::{
|
|||||||
IdeviceError, IdeviceService, heartbeat::HeartbeatClient, provider::IdeviceProvider,
|
IdeviceError, IdeviceService, heartbeat::HeartbeatClient, provider::IdeviceProvider,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, IdeviceHandle, RUNTIME, ffi_err, provider::IdeviceProviderHandle};
|
use crate::{
|
||||||
|
IdeviceFfiError, IdeviceHandle, ffi_err, provider::IdeviceProviderHandle, run_sync_local,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct HeartbeatClientHandle(pub HeartbeatClient);
|
pub struct HeartbeatClientHandle(pub HeartbeatClient);
|
||||||
|
|
||||||
@@ -28,11 +30,11 @@ pub unsafe extern "C" fn heartbeat_connect(
|
|||||||
client: *mut *mut HeartbeatClientHandle,
|
client: *mut *mut HeartbeatClientHandle,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if provider.is_null() || client.is_null() {
|
if provider.is_null() || client.is_null() {
|
||||||
log::error!("Null pointer provided");
|
tracing::error!("Null pointer provided");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<HeartbeatClient, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<HeartbeatClient, IdeviceError> = run_sync_local(async move {
|
||||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
// Connect using the reference
|
// Connect using the reference
|
||||||
HeartbeatClient::connect(provider_ref).await
|
HeartbeatClient::connect(provider_ref).await
|
||||||
@@ -95,7 +97,7 @@ pub unsafe extern "C" fn heartbeat_send_polo(
|
|||||||
if client.is_null() {
|
if client.is_null() {
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.send_polo().await
|
client_ref.send_polo().await
|
||||||
});
|
});
|
||||||
@@ -126,7 +128,7 @@ pub unsafe extern "C" fn heartbeat_get_marco(
|
|||||||
if client.is_null() || new_interval.is_null() {
|
if client.is_null() || new_interval.is_null() {
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
let res: Result<u64, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<u64, IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.get_marco(interval).await
|
client_ref.get_marco(interval).await
|
||||||
});
|
});
|
||||||
@@ -150,7 +152,7 @@ pub unsafe extern "C" fn heartbeat_get_marco(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn heartbeat_client_free(handle: *mut HeartbeatClientHandle) {
|
pub unsafe extern "C" fn heartbeat_client_free(handle: *mut HeartbeatClientHandle) {
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
log::debug!("Freeing installation_proxy_client");
|
tracing::debug!("Freeing installation_proxy_client");
|
||||||
let _ = unsafe { Box::from_raw(handle) };
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
183
ffi/src/house_arrest.rs
Normal file
183
ffi/src/house_arrest.rs
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
ffi::{CStr, c_char},
|
||||||
|
ptr::null_mut,
|
||||||
|
};
|
||||||
|
|
||||||
|
use idevice::{
|
||||||
|
IdeviceError, IdeviceService, afc::AfcClient, house_arrest::HouseArrestClient,
|
||||||
|
provider::IdeviceProvider,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
IdeviceFfiError, IdeviceHandle, afc::AfcClientHandle, ffi_err, provider::IdeviceProviderHandle,
|
||||||
|
run_sync_local,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct HouseArrestClientHandle(pub HouseArrestClient);
|
||||||
|
|
||||||
|
/// Connects to the House Arrest service using a TCP provider
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`provider`] - An IdeviceProvider
|
||||||
|
/// * [`client`] - On success, will be set to point to a newly allocated HouseArrestClient handle
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `provider` must be a valid pointer to a handle allocated by this library
|
||||||
|
/// `client` must be a valid, non-null pointer to a location where the handle will be stored
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn house_arrest_client_connect(
|
||||||
|
provider: *mut IdeviceProviderHandle,
|
||||||
|
client: *mut *mut HouseArrestClientHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if provider.is_null() || client.is_null() {
|
||||||
|
tracing::error!("Null pointer provided");
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = run_sync_local(async {
|
||||||
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
|
|
||||||
|
HouseArrestClient::connect(provider_ref).await
|
||||||
|
});
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(r) => {
|
||||||
|
let boxed = Box::new(HouseArrestClientHandle(r));
|
||||||
|
unsafe { *client = Box::into_raw(boxed) };
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new HouseArrestClient from an existing Idevice connection
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`socket`] - An IdeviceSocket handle
|
||||||
|
/// * [`client`] - On success, will be set to point to a newly allocated HouseArrestClient handle
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `socket` must be a valid pointer to a handle allocated by this library
|
||||||
|
/// `client` must be a valid, non-null pointer to a location where the handle will be stored
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn house_arrest_client_new(
|
||||||
|
socket: *mut IdeviceHandle,
|
||||||
|
client: *mut *mut HouseArrestClientHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if socket.is_null() || client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
let socket = unsafe { Box::from_raw(socket) }.0;
|
||||||
|
let r = HouseArrestClient::new(socket);
|
||||||
|
let boxed = Box::new(HouseArrestClientHandle(r));
|
||||||
|
unsafe { *client = Box::into_raw(boxed) };
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vends a container for an app
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`client`] - The House Arrest client
|
||||||
|
/// * [`bundle_id`] - The bundle ID to vend for
|
||||||
|
/// * [`afc_client`] - The new AFC client for the underlying connection
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a allocated by this library
|
||||||
|
/// `bundle_id` must be a NULL-terminated string
|
||||||
|
/// `afc_client` must be a valid, non-null pointer where the new AFC client will be stored
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn house_arrest_vend_container(
|
||||||
|
client: *mut HouseArrestClientHandle,
|
||||||
|
bundle_id: *const c_char,
|
||||||
|
afc_client: *mut *mut AfcClientHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() || bundle_id.is_null() || afc_client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let bundle_id = unsafe { CStr::from_ptr(bundle_id) }.to_string_lossy();
|
||||||
|
let client_ref = unsafe { Box::from_raw(client) }.0; // take ownership and drop
|
||||||
|
|
||||||
|
let res: Result<AfcClient, IdeviceError> =
|
||||||
|
run_sync_local(async move { client_ref.vend_container(bundle_id).await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(a) => {
|
||||||
|
let a = Box::into_raw(Box::new(AfcClientHandle(a)));
|
||||||
|
unsafe { *afc_client = a };
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
ffi_err!(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vends documents for an app
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`client`] - The House Arrest client
|
||||||
|
/// * [`bundle_id`] - The bundle ID to vend for
|
||||||
|
/// * [`afc_client`] - The new AFC client for the underlying connection
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a allocated by this library
|
||||||
|
/// `bundle_id` must be a NULL-terminated string
|
||||||
|
/// `afc_client` must be a valid, non-null pointer where the new AFC client will be stored
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn house_arrest_vend_documents(
|
||||||
|
client: *mut HouseArrestClientHandle,
|
||||||
|
bundle_id: *const c_char,
|
||||||
|
afc_client: *mut *mut AfcClientHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() || bundle_id.is_null() || afc_client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let bundle_id = unsafe { CStr::from_ptr(bundle_id) }.to_string_lossy();
|
||||||
|
let client_ref = unsafe { Box::from_raw(client) }.0; // take ownership and drop
|
||||||
|
|
||||||
|
let res: Result<AfcClient, IdeviceError> =
|
||||||
|
run_sync_local(async move { client_ref.vend_documents(bundle_id).await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(a) => {
|
||||||
|
let a = Box::into_raw(Box::new(AfcClientHandle(a)));
|
||||||
|
unsafe { *afc_client = a };
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
ffi_err!(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frees an HouseArrestClient handle
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`handle`] - The handle to free
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `handle` must be a valid pointer to the handle that was allocated by this library,
|
||||||
|
/// or NULL (in which case this function does nothing)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn house_arrest_client_free(handle: *mut HouseArrestClientHandle) {
|
||||||
|
if !handle.is_null() {
|
||||||
|
tracing::debug!("Freeing house_arrest_client");
|
||||||
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,9 @@ use idevice::{
|
|||||||
};
|
};
|
||||||
use plist_ffi::{PlistWrapper, plist_t};
|
use plist_ffi::{PlistWrapper, plist_t};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, IdeviceHandle, RUNTIME, ffi_err, provider::IdeviceProviderHandle};
|
use crate::{
|
||||||
|
IdeviceFfiError, IdeviceHandle, ffi_err, provider::IdeviceProviderHandle, run_sync_local,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct InstallationProxyClientHandle(pub InstallationProxyClient);
|
pub struct InstallationProxyClientHandle(pub InstallationProxyClient);
|
||||||
|
|
||||||
@@ -30,11 +32,11 @@ pub unsafe extern "C" fn installation_proxy_connect(
|
|||||||
client: *mut *mut InstallationProxyClientHandle,
|
client: *mut *mut InstallationProxyClientHandle,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if provider.is_null() || client.is_null() {
|
if provider.is_null() || client.is_null() {
|
||||||
log::error!("Null pointer provided");
|
tracing::error!("Null pointer provided");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<InstallationProxyClient, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<InstallationProxyClient, IdeviceError> = run_sync_local(async move {
|
||||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
InstallationProxyClient::connect(provider_ref).await
|
InstallationProxyClient::connect(provider_ref).await
|
||||||
});
|
});
|
||||||
@@ -101,7 +103,7 @@ pub unsafe extern "C" fn installation_proxy_get_apps(
|
|||||||
out_result_len: *mut libc::size_t,
|
out_result_len: *mut libc::size_t,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if client.is_null() || out_result.is_null() || out_result_len.is_null() {
|
if client.is_null() || out_result.is_null() || out_result_len.is_null() {
|
||||||
log::error!("Invalid arguments: {client:?}, {out_result:?}");
|
tracing::error!("Invalid arguments: {client:?}, {out_result:?}");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
let client = unsafe { &mut *client };
|
let client = unsafe { &mut *client };
|
||||||
@@ -134,7 +136,7 @@ pub unsafe extern "C" fn installation_proxy_get_apps(
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<Vec<plist_t>, IdeviceError> = RUNTIME.block_on(async {
|
let res: Result<Vec<plist_t>, IdeviceError> = run_sync_local(async {
|
||||||
client.0.get_apps(app_type, bundle_ids).await.map(|apps| {
|
client.0.get_apps(app_type, bundle_ids).await.map(|apps| {
|
||||||
apps.into_values()
|
apps.into_values()
|
||||||
.map(|v| PlistWrapper::new_node(v).into_ptr())
|
.map(|v| PlistWrapper::new_node(v).into_ptr())
|
||||||
@@ -143,7 +145,8 @@ pub unsafe extern "C" fn installation_proxy_get_apps(
|
|||||||
});
|
});
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(mut r) => {
|
Ok(r) => {
|
||||||
|
let mut r = r.into_boxed_slice();
|
||||||
let ptr = r.as_mut_ptr();
|
let ptr = r.as_mut_ptr();
|
||||||
let len = r.len();
|
let len = r.len();
|
||||||
std::mem::forget(r);
|
std::mem::forget(r);
|
||||||
@@ -171,7 +174,7 @@ pub unsafe extern "C" fn installation_proxy_client_free(
|
|||||||
handle: *mut InstallationProxyClientHandle,
|
handle: *mut InstallationProxyClientHandle,
|
||||||
) {
|
) {
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
log::debug!("Freeing installation_proxy_client");
|
tracing::debug!("Freeing installation_proxy_client");
|
||||||
let _ = unsafe { Box::from_raw(handle) };
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,7 +213,7 @@ pub unsafe extern "C" fn installation_proxy_install(
|
|||||||
}
|
}
|
||||||
.map(|x| x.borrow_self().clone());
|
.map(|x| x.borrow_self().clone());
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async {
|
let res = run_sync_local(async {
|
||||||
unsafe { &mut *client }
|
unsafe { &mut *client }
|
||||||
.0
|
.0
|
||||||
.install(package_path, options)
|
.install(package_path, options)
|
||||||
@@ -261,7 +264,7 @@ pub unsafe extern "C" fn installation_proxy_install_with_callback(
|
|||||||
}
|
}
|
||||||
.map(|x| x.borrow_self().clone());
|
.map(|x| x.borrow_self().clone());
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async {
|
let res = run_sync_local(async {
|
||||||
let callback_wrapper = |(progress, context)| async move {
|
let callback_wrapper = |(progress, context)| async move {
|
||||||
callback(progress, context);
|
callback(progress, context);
|
||||||
};
|
};
|
||||||
@@ -312,7 +315,7 @@ pub unsafe extern "C" fn installation_proxy_upgrade(
|
|||||||
}
|
}
|
||||||
.map(|x| x.borrow_self().clone());
|
.map(|x| x.borrow_self().clone());
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async {
|
let res = run_sync_local(async {
|
||||||
unsafe { &mut *client }
|
unsafe { &mut *client }
|
||||||
.0
|
.0
|
||||||
.upgrade(package_path, options)
|
.upgrade(package_path, options)
|
||||||
@@ -363,7 +366,7 @@ pub unsafe extern "C" fn installation_proxy_upgrade_with_callback(
|
|||||||
}
|
}
|
||||||
.map(|x| x.borrow_self().clone());
|
.map(|x| x.borrow_self().clone());
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async {
|
let res = run_sync_local(async {
|
||||||
let callback_wrapper = |(progress, context)| async move {
|
let callback_wrapper = |(progress, context)| async move {
|
||||||
callback(progress, context);
|
callback(progress, context);
|
||||||
};
|
};
|
||||||
@@ -414,7 +417,7 @@ pub unsafe extern "C" fn installation_proxy_uninstall(
|
|||||||
}
|
}
|
||||||
.map(|x| x.borrow_self().clone());
|
.map(|x| x.borrow_self().clone());
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async {
|
let res = run_sync_local(async {
|
||||||
unsafe { &mut *client }
|
unsafe { &mut *client }
|
||||||
.0
|
.0
|
||||||
.uninstall(bundle_id, options)
|
.uninstall(bundle_id, options)
|
||||||
@@ -465,7 +468,7 @@ pub unsafe extern "C" fn installation_proxy_uninstall_with_callback(
|
|||||||
}
|
}
|
||||||
.map(|x| x.borrow_self().clone());
|
.map(|x| x.borrow_self().clone());
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async {
|
let res = run_sync_local(async {
|
||||||
let callback_wrapper = |(progress, context)| async move {
|
let callback_wrapper = |(progress, context)| async move {
|
||||||
callback(progress, context);
|
callback(progress, context);
|
||||||
};
|
};
|
||||||
@@ -527,7 +530,7 @@ pub unsafe extern "C" fn installation_proxy_check_capabilities_match(
|
|||||||
}
|
}
|
||||||
.map(|x| x.borrow_self().clone());
|
.map(|x| x.borrow_self().clone());
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async {
|
let res = run_sync_local(async {
|
||||||
unsafe { &mut *client }
|
unsafe { &mut *client }
|
||||||
.0
|
.0
|
||||||
.check_capabilities_match(capabilities, options)
|
.check_capabilities_match(capabilities, options)
|
||||||
@@ -577,7 +580,7 @@ pub unsafe extern "C" fn installation_proxy_browse(
|
|||||||
}
|
}
|
||||||
.map(|x| x.borrow_self().clone());
|
.map(|x| x.borrow_self().clone());
|
||||||
|
|
||||||
let res: Result<Vec<plist_t>, IdeviceError> = RUNTIME.block_on(async {
|
let res: Result<Vec<plist_t>, IdeviceError> = run_sync_local(async {
|
||||||
unsafe { &mut *client }.0.browse(options).await.map(|apps| {
|
unsafe { &mut *client }.0.browse(options).await.map(|apps| {
|
||||||
apps.into_iter()
|
apps.into_iter()
|
||||||
.map(|v| PlistWrapper::new_node(v).into_ptr())
|
.map(|v| PlistWrapper::new_node(v).into_ptr())
|
||||||
|
|||||||
147
ffi/src/lib.rs
147
ffi/src/lib.rs
@@ -12,13 +12,17 @@ pub mod core_device;
|
|||||||
pub mod core_device_proxy;
|
pub mod core_device_proxy;
|
||||||
#[cfg(feature = "debug_proxy")]
|
#[cfg(feature = "debug_proxy")]
|
||||||
pub mod debug_proxy;
|
pub mod debug_proxy;
|
||||||
|
#[cfg(feature = "diagnostics_relay")]
|
||||||
|
pub mod diagnostics_relay;
|
||||||
|
#[cfg(feature = "dvt")]
|
||||||
|
pub mod dvt;
|
||||||
mod errors;
|
mod errors;
|
||||||
#[cfg(feature = "heartbeat")]
|
#[cfg(feature = "heartbeat")]
|
||||||
pub mod heartbeat;
|
pub mod heartbeat;
|
||||||
|
#[cfg(feature = "house_arrest")]
|
||||||
|
pub mod house_arrest;
|
||||||
#[cfg(feature = "installation_proxy")]
|
#[cfg(feature = "installation_proxy")]
|
||||||
pub mod installation_proxy;
|
pub mod installation_proxy;
|
||||||
#[cfg(feature = "location_simulation")]
|
|
||||||
pub mod location_simulation;
|
|
||||||
pub mod lockdown;
|
pub mod lockdown;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
#[cfg(feature = "misagent")]
|
#[cfg(feature = "misagent")]
|
||||||
@@ -28,13 +32,11 @@ pub mod mobile_image_mounter;
|
|||||||
#[cfg(feature = "syslog_relay")]
|
#[cfg(feature = "syslog_relay")]
|
||||||
pub mod os_trace_relay;
|
pub mod os_trace_relay;
|
||||||
mod pairing_file;
|
mod pairing_file;
|
||||||
#[cfg(feature = "dvt")]
|
|
||||||
pub mod process_control;
|
|
||||||
pub mod provider;
|
pub mod provider;
|
||||||
#[cfg(feature = "dvt")]
|
|
||||||
pub mod remote_server;
|
|
||||||
#[cfg(feature = "xpc")]
|
#[cfg(feature = "xpc")]
|
||||||
pub mod rsd;
|
pub mod rsd;
|
||||||
|
#[cfg(feature = "screenshotr")]
|
||||||
|
pub mod screenshotr;
|
||||||
#[cfg(feature = "springboardservices")]
|
#[cfg(feature = "springboardservices")]
|
||||||
pub mod springboardservices;
|
pub mod springboardservices;
|
||||||
#[cfg(feature = "syslog_relay")]
|
#[cfg(feature = "syslog_relay")]
|
||||||
@@ -50,8 +52,9 @@ pub use pairing_file::*;
|
|||||||
|
|
||||||
use idevice::{Idevice, IdeviceSocket, ReadWrite};
|
use idevice::{Idevice, IdeviceSocket, ReadWrite};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use plist_ffi::PlistWrapper;
|
||||||
use std::{
|
use std::{
|
||||||
ffi::{CStr, CString, c_char},
|
ffi::{CStr, CString, c_char, c_void},
|
||||||
ptr::null_mut,
|
ptr::null_mut,
|
||||||
};
|
};
|
||||||
use tokio::runtime::{self, Runtime};
|
use tokio::runtime::{self, Runtime};
|
||||||
@@ -59,7 +62,7 @@ use tokio::runtime::{self, Runtime};
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use crate::util::{idevice_sockaddr, idevice_socklen_t};
|
use crate::util::{idevice_sockaddr, idevice_socklen_t};
|
||||||
|
|
||||||
static RUNTIME: Lazy<Runtime> = Lazy::new(|| {
|
static GLOBAL_RUNTIME: Lazy<Runtime> = Lazy::new(|| {
|
||||||
runtime::Builder::new_multi_thread()
|
runtime::Builder::new_multi_thread()
|
||||||
.enable_io()
|
.enable_io()
|
||||||
.enable_time()
|
.enable_time()
|
||||||
@@ -67,6 +70,39 @@ static RUNTIME: Lazy<Runtime> = Lazy::new(|| {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static LOCAL_RUNTIME: Lazy<Runtime> = Lazy::new(|| {
|
||||||
|
runtime::Builder::new_multi_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Spawn the future on the global runtime and block current (FFI) thread until result.
|
||||||
|
/// F and R must be Send + 'static.
|
||||||
|
pub fn run_sync<F, R>(fut: F) -> R
|
||||||
|
where
|
||||||
|
F: std::future::Future<Output = R> + Send + 'static,
|
||||||
|
R: Send + 'static,
|
||||||
|
{
|
||||||
|
let (tx, rx) = std::sync::mpsc::sync_channel(1);
|
||||||
|
|
||||||
|
GLOBAL_RUNTIME.handle().spawn(async move {
|
||||||
|
let res = fut.await;
|
||||||
|
// best-effort send; ignore if receiver dropped
|
||||||
|
let _ = tx.send(res);
|
||||||
|
});
|
||||||
|
|
||||||
|
rx.recv().expect("runtime worker panicked")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_sync_local<F, R>(fut: F) -> R
|
||||||
|
where
|
||||||
|
F: std::future::Future<Output = R>,
|
||||||
|
R: 'static,
|
||||||
|
{
|
||||||
|
LOCAL_RUNTIME.block_on(fut)
|
||||||
|
}
|
||||||
|
|
||||||
pub const LOCKDOWN_PORT: u16 = 62078;
|
pub const LOCKDOWN_PORT: u16 = 62078;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@@ -126,6 +162,47 @@ pub unsafe extern "C" fn idevice_new(
|
|||||||
null_mut()
|
null_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates an Idevice object from a socket file descriptor
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// The socket FD must be valid.
|
||||||
|
/// The pointers must be valid and non-null.
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_from_fd(
|
||||||
|
fd: i32,
|
||||||
|
label: *const c_char,
|
||||||
|
idevice: *mut *mut IdeviceHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if label.is_null() || idevice.is_null() || fd == 0 {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get socket ownership
|
||||||
|
let fd = unsafe { libc::dup(fd) };
|
||||||
|
let socket = unsafe { <std::net::TcpStream as std::os::fd::FromRawFd>::from_raw_fd(fd) };
|
||||||
|
if let Err(e) = socket.set_nonblocking(true) {
|
||||||
|
return ffi_err!(e);
|
||||||
|
}
|
||||||
|
let socket = match run_sync(async move { tokio::net::TcpStream::from_std(socket) }) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => return ffi_err!(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert C string to Rust string
|
||||||
|
let c_str = match unsafe { CStr::from_ptr(label).to_str() } {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create new Idevice instance
|
||||||
|
let dev = Idevice::new(Box::new(socket), c_str);
|
||||||
|
let boxed = Box::new(IdeviceHandle(dev));
|
||||||
|
unsafe { *idevice = Box::into_raw(boxed) };
|
||||||
|
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new Idevice connection
|
/// Creates a new Idevice connection
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -152,7 +229,7 @@ pub unsafe extern "C" fn idevice_new_tcp_socket(
|
|||||||
use crate::util::SockAddr;
|
use crate::util::SockAddr;
|
||||||
|
|
||||||
if addr.is_null() || label.is_null() || idevice.is_null() {
|
if addr.is_null() || label.is_null() || idevice.is_null() {
|
||||||
log::error!("null pointer(s) to idevice_new_tcp_socket");
|
tracing::error!("null pointer(s) to idevice_new_tcp_socket");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
let addr = addr as *const SockAddr;
|
let addr = addr as *const SockAddr;
|
||||||
@@ -167,7 +244,7 @@ pub unsafe extern "C" fn idevice_new_tcp_socket(
|
|||||||
Err(e) => return ffi_err!(e),
|
Err(e) => return ffi_err!(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
let device = RUNTIME.block_on(async move {
|
let device = run_sync(async move {
|
||||||
let stream = tokio::net::TcpStream::connect(addr).await?;
|
let stream = tokio::net::TcpStream::connect(addr).await?;
|
||||||
Ok::<idevice::Idevice, idevice::IdeviceError>(idevice::Idevice::new(
|
Ok::<idevice::Idevice, idevice::IdeviceError>(idevice::Idevice::new(
|
||||||
Box::new(stream),
|
Box::new(stream),
|
||||||
@@ -210,7 +287,7 @@ pub unsafe extern "C" fn idevice_get_type(
|
|||||||
let dev = unsafe { &mut (*idevice).0 };
|
let dev = unsafe { &mut (*idevice).0 };
|
||||||
|
|
||||||
// Run the get_type method in the runtime
|
// Run the get_type method in the runtime
|
||||||
let result = RUNTIME.block_on(async { dev.get_type().await });
|
let result = run_sync(async { dev.get_type().await });
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(type_str) => match CString::new(type_str) {
|
Ok(type_str) => match CString::new(type_str) {
|
||||||
@@ -244,7 +321,7 @@ pub unsafe extern "C" fn idevice_rsd_checkin(idevice: *mut IdeviceHandle) -> *mu
|
|||||||
let dev = unsafe { &mut (*idevice).0 };
|
let dev = unsafe { &mut (*idevice).0 };
|
||||||
|
|
||||||
// Run the rsd_checkin method in the runtime
|
// Run the rsd_checkin method in the runtime
|
||||||
let result = RUNTIME.block_on(async { dev.rsd_checkin().await });
|
let result = run_sync(async { dev.rsd_checkin().await });
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
@@ -268,6 +345,7 @@ pub unsafe extern "C" fn idevice_rsd_checkin(idevice: *mut IdeviceHandle) -> *mu
|
|||||||
pub unsafe extern "C" fn idevice_start_session(
|
pub unsafe extern "C" fn idevice_start_session(
|
||||||
idevice: *mut IdeviceHandle,
|
idevice: *mut IdeviceHandle,
|
||||||
pairing_file: *const IdevicePairingFile,
|
pairing_file: *const IdevicePairingFile,
|
||||||
|
legacy: bool,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if idevice.is_null() || pairing_file.is_null() {
|
if idevice.is_null() || pairing_file.is_null() {
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
@@ -280,7 +358,7 @@ pub unsafe extern "C" fn idevice_start_session(
|
|||||||
let pf = unsafe { &(*pairing_file).0 };
|
let pf = unsafe { &(*pairing_file).0 };
|
||||||
|
|
||||||
// Run the start_session method in the runtime
|
// Run the start_session method in the runtime
|
||||||
let result = RUNTIME.block_on(async { dev.start_session(pf).await });
|
let result = run_sync(async move { dev.start_session(pf, legacy).await });
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
@@ -303,6 +381,17 @@ pub unsafe extern "C" fn idevice_free(idevice: *mut IdeviceHandle) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Frees a stream handle
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Pass a valid handle allocated by this library
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_stream_free(stream_handle: *mut ReadWriteOpaque) {
|
||||||
|
if !stream_handle.is_null() {
|
||||||
|
let _ = unsafe { Box::from_raw(stream_handle) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Frees a string allocated by this library
|
/// Frees a string allocated by this library
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -329,6 +418,36 @@ pub unsafe extern "C" fn idevice_string_free(string: *mut c_char) {
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn idevice_data_free(data: *mut u8, len: usize) {
|
pub unsafe extern "C" fn idevice_data_free(data: *mut u8, len: usize) {
|
||||||
if !data.is_null() {
|
if !data.is_null() {
|
||||||
let _ = unsafe { std::slice::from_raw_parts(data, len) };
|
let _ = unsafe { Vec::from_raw_parts(data, len, len) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frees an array of plists allocated by this library
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `data` must be a pointer to data allocated by this library,
|
||||||
|
/// NOT data allocated by libplist.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_plist_array_free(plists: *mut plist_t, len: usize) {
|
||||||
|
if !plists.is_null() {
|
||||||
|
let data = unsafe { std::slice::from_raw_parts(plists, len) };
|
||||||
|
for x in data {
|
||||||
|
unsafe { plist_ffi::creation::plist_free((*x) as *mut PlistWrapper) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frees a slice of pointers allocated by this library that had an underlying
|
||||||
|
/// vec creation.
|
||||||
|
///
|
||||||
|
/// The following functions use an underlying vec and are safe to use:
|
||||||
|
/// - idevice_usbmuxd_get_devices
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Pass a valid pointer passed by the Vec creating functions
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_outer_slice_free(slice: *mut c_void, len: usize) {
|
||||||
|
if !slice.is_null() {
|
||||||
|
let _ = unsafe { Vec::from_raw_parts(slice, len, len) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use idevice::{IdeviceError, IdeviceService, lockdown::LockdownClient, provider::
|
|||||||
use plist_ffi::plist_t;
|
use plist_ffi::plist_t;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
IdeviceFfiError, IdeviceHandle, IdevicePairingFile, RUNTIME, ffi_err,
|
IdeviceFfiError, IdeviceHandle, IdevicePairingFile, ffi_err, provider::IdeviceProviderHandle,
|
||||||
provider::IdeviceProviderHandle,
|
run_sync_local,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct LockdowndClientHandle(pub LockdownClient);
|
pub struct LockdowndClientHandle(pub LockdownClient);
|
||||||
@@ -30,11 +30,11 @@ pub unsafe extern "C" fn lockdownd_connect(
|
|||||||
client: *mut *mut LockdowndClientHandle,
|
client: *mut *mut LockdowndClientHandle,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if provider.is_null() || client.is_null() {
|
if provider.is_null() || client.is_null() {
|
||||||
log::error!("Null pointer provided");
|
tracing::error!("Null pointer provided");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<LockdownClient, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<LockdownClient, IdeviceError> = run_sync_local(async move {
|
||||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
LockdownClient::connect(provider_ref).await
|
LockdownClient::connect(provider_ref).await
|
||||||
});
|
});
|
||||||
@@ -97,7 +97,7 @@ pub unsafe extern "C" fn lockdownd_start_session(
|
|||||||
client: *mut LockdowndClientHandle,
|
client: *mut LockdowndClientHandle,
|
||||||
pairing_file: *mut IdevicePairingFile,
|
pairing_file: *mut IdevicePairingFile,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
let pairing_file_ref = unsafe { &(*pairing_file).0 };
|
let pairing_file_ref = unsafe { &(*pairing_file).0 };
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ pub unsafe extern "C" fn lockdownd_start_service(
|
|||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.into_owned();
|
.into_owned();
|
||||||
|
|
||||||
let res: Result<(u16, bool), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(u16, bool), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.start_service(identifier).await
|
client_ref.start_service(identifier).await
|
||||||
});
|
});
|
||||||
@@ -179,7 +179,7 @@ pub unsafe extern "C" fn lockdownd_get_value(
|
|||||||
domain: *const libc::c_char,
|
domain: *const libc::c_char,
|
||||||
out_plist: *mut plist_t,
|
out_plist: *mut plist_t,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if out_plist.is_null() {
|
if client.is_null() || out_plist.is_null() {
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ pub unsafe extern "C" fn lockdownd_get_value(
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<plist::Value, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<plist::Value, IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.get_value(value, domain).await
|
client_ref.get_value(value, domain).await
|
||||||
});
|
});
|
||||||
@@ -221,6 +221,89 @@ pub unsafe extern "C" fn lockdownd_get_value(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tells the device to enter recovery mode
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid LockdowndClient handle
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn lockdownd_enter_recovery(
|
||||||
|
client: *mut LockdowndClientHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
|
client_ref.enter_recovery().await
|
||||||
|
});
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(_) => null_mut(),
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a value in lockdownd
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid LockdowndClient handle
|
||||||
|
/// * `key` - The key to set (null-terminated string)
|
||||||
|
/// * `value` - The value to set as a plist
|
||||||
|
/// * `domain` - The domain to set in (null-terminated string, optional)
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
/// `key` must be a valid null-terminated string
|
||||||
|
/// `value` must be a valid plist
|
||||||
|
/// `domain` must be a valid null-terminated string or NULL
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn lockdownd_set_value(
|
||||||
|
client: *mut LockdowndClientHandle,
|
||||||
|
key: *const libc::c_char,
|
||||||
|
value: plist_t,
|
||||||
|
domain: *const libc::c_char,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() || key.is_null() || value.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = match unsafe { std::ffi::CStr::from_ptr(key) }.to_str() {
|
||||||
|
Ok(k) => k,
|
||||||
|
Err(_) => return ffi_err!(IdeviceError::InvalidCString),
|
||||||
|
};
|
||||||
|
|
||||||
|
let domain = if domain.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(match unsafe { std::ffi::CStr::from_ptr(domain) }.to_str() {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(_) => return ffi_err!(IdeviceError::InvalidCString),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = unsafe { &mut *value }.borrow_self().clone();
|
||||||
|
|
||||||
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
|
client_ref.set_value(key, value, domain).await
|
||||||
|
});
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(_) => null_mut(),
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Frees a LockdowndClient handle
|
/// Frees a LockdowndClient handle
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -232,7 +315,7 @@ pub unsafe extern "C" fn lockdownd_get_value(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn lockdownd_client_free(handle: *mut LockdowndClientHandle) {
|
pub unsafe extern "C" fn lockdownd_client_free(handle: *mut LockdowndClientHandle) {
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
log::debug!("Freeing lockdownd_client");
|
tracing::debug!("Freeing lockdownd_client");
|
||||||
let _ = unsafe { Box::from_raw(handle) };
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,75 +3,13 @@
|
|||||||
use std::{
|
use std::{
|
||||||
ffi::{CStr, c_char},
|
ffi::{CStr, c_char},
|
||||||
fs::File,
|
fs::File,
|
||||||
|
sync::Once,
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::LevelFilter;
|
use tracing::Level;
|
||||||
use simplelog::{
|
use tracing_appender::non_blocking;
|
||||||
ColorChoice, CombinedLogger, Config, SharedLogger, TermLogger, TerminalMode, WriteLogger,
|
use tracing_subscriber::{EnvFilter, Layer};
|
||||||
};
|
use tracing_subscriber::{Registry, fmt, layer::SubscriberExt, util::SubscriberInitExt};
|
||||||
|
|
||||||
/// Initializes the logger
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
/// * [`console_level`] - The level to log to the file
|
|
||||||
/// * [`file_level`] - The level to log to the file
|
|
||||||
/// * [`file_path`] - If not null, the file to write logs to
|
|
||||||
///
|
|
||||||
/// ## Log Level
|
|
||||||
/// 0. Disabled
|
|
||||||
/// 1. Error
|
|
||||||
/// 2. Warn
|
|
||||||
/// 3. Info
|
|
||||||
/// 4. Debug
|
|
||||||
/// 5. Trace
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// 0 for success, -1 if the file couldn't be created, -2 if a logger has been initialized, -3 for invalid path string
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// Pass a valid CString for file_path. Pass valid log levels according to the enum
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe extern "C" fn idevice_init_logger(
|
|
||||||
console_level: IdeviceLogLevel,
|
|
||||||
file_level: IdeviceLogLevel,
|
|
||||||
file_path: *mut c_char,
|
|
||||||
) -> IdeviceLoggerError {
|
|
||||||
let mut loggers: Vec<Box<dyn SharedLogger>> = Vec::new();
|
|
||||||
let level: LevelFilter = console_level.into();
|
|
||||||
loggers.push(TermLogger::new(
|
|
||||||
level,
|
|
||||||
Config::default(),
|
|
||||||
TerminalMode::Mixed,
|
|
||||||
ColorChoice::Auto,
|
|
||||||
));
|
|
||||||
|
|
||||||
if !file_path.is_null() {
|
|
||||||
let file_path = match unsafe { CStr::from_ptr(file_path) }.to_str() {
|
|
||||||
Ok(f) => f.to_string(),
|
|
||||||
Err(_) => {
|
|
||||||
return IdeviceLoggerError::InvalidPathString;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let level: LevelFilter = file_level.into();
|
|
||||||
loggers.push(WriteLogger::new(
|
|
||||||
level,
|
|
||||||
Config::default(),
|
|
||||||
match File::create(file_path) {
|
|
||||||
Ok(f) => f,
|
|
||||||
Err(e) => {
|
|
||||||
println!("Failed to create path: {e:?}");
|
|
||||||
return IdeviceLoggerError::FileError;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if CombinedLogger::init(loggers).is_err() {
|
|
||||||
IdeviceLoggerError::AlreadyInitialized
|
|
||||||
} else {
|
|
||||||
IdeviceLoggerError::Success
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub enum IdeviceLoggerError {
|
pub enum IdeviceLoggerError {
|
||||||
@@ -92,33 +30,113 @@ pub enum IdeviceLogLevel {
|
|||||||
Trace = 5,
|
Trace = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u8> for IdeviceLogLevel {
|
impl From<IdeviceLogLevel> for Level {
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
|
||||||
Ok(match value {
|
|
||||||
0 => Self::Disabled,
|
|
||||||
1 => Self::ErrorLevel,
|
|
||||||
2 => Self::Warn,
|
|
||||||
3 => Self::Info,
|
|
||||||
4 => Self::Debug,
|
|
||||||
5 => Self::Trace,
|
|
||||||
_ => {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<IdeviceLogLevel> for LevelFilter {
|
|
||||||
fn from(value: IdeviceLogLevel) -> Self {
|
fn from(value: IdeviceLogLevel) -> Self {
|
||||||
match value {
|
match value {
|
||||||
IdeviceLogLevel::Disabled => LevelFilter::Off,
|
IdeviceLogLevel::Disabled => Level::ERROR, // won't matter, filter will disable
|
||||||
IdeviceLogLevel::ErrorLevel => LevelFilter::Error,
|
IdeviceLogLevel::ErrorLevel => Level::ERROR,
|
||||||
IdeviceLogLevel::Warn => LevelFilter::Warn,
|
IdeviceLogLevel::Warn => Level::WARN,
|
||||||
IdeviceLogLevel::Info => LevelFilter::Info,
|
IdeviceLogLevel::Info => Level::INFO,
|
||||||
IdeviceLogLevel::Debug => LevelFilter::Debug,
|
IdeviceLogLevel::Debug => Level::DEBUG,
|
||||||
IdeviceLogLevel::Trace => LevelFilter::Trace,
|
IdeviceLogLevel::Trace => Level::TRACE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IdeviceLogLevel {
|
||||||
|
fn as_filter(&self) -> String {
|
||||||
|
match self {
|
||||||
|
IdeviceLogLevel::Disabled => "off",
|
||||||
|
IdeviceLogLevel::ErrorLevel => "error",
|
||||||
|
IdeviceLogLevel::Warn => "warn",
|
||||||
|
IdeviceLogLevel::Info => "info",
|
||||||
|
IdeviceLogLevel::Debug => "debug",
|
||||||
|
IdeviceLogLevel::Trace => "trace",
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensures we only init once
|
||||||
|
static INIT: Once = Once::new();
|
||||||
|
static mut GUARDS: Option<(
|
||||||
|
Option<tracing_appender::non_blocking::WorkerGuard>,
|
||||||
|
Option<tracing_appender::non_blocking::WorkerGuard>,
|
||||||
|
)> = None;
|
||||||
|
|
||||||
|
/// Initializes the global logger
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Pass a valid file path string
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_init_logger(
|
||||||
|
console_level: IdeviceLogLevel,
|
||||||
|
file_level: IdeviceLogLevel,
|
||||||
|
file_path: *mut c_char,
|
||||||
|
) -> IdeviceLoggerError {
|
||||||
|
let mut init_result = IdeviceLoggerError::Success;
|
||||||
|
|
||||||
|
INIT.call_once(|| {
|
||||||
|
let console_filter = console_level.as_filter();
|
||||||
|
let file_filter = file_level.as_filter();
|
||||||
|
|
||||||
|
let mut layers = Vec::new();
|
||||||
|
let mut console_guard = None;
|
||||||
|
let mut file_guard = None;
|
||||||
|
|
||||||
|
// ---- Console layer ----
|
||||||
|
if console_level != IdeviceLogLevel::Disabled {
|
||||||
|
let (non_blocking, guard) = non_blocking(std::io::stdout());
|
||||||
|
console_guard = Some(guard);
|
||||||
|
let console_layer = fmt::layer()
|
||||||
|
.with_writer(non_blocking)
|
||||||
|
.with_ansi(true)
|
||||||
|
.with_target(false)
|
||||||
|
.with_target(true)
|
||||||
|
.with_filter(EnvFilter::new(console_filter));
|
||||||
|
layers.push(console_layer.boxed());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- File layer ----
|
||||||
|
if !file_path.is_null() && file_level != IdeviceLogLevel::Disabled {
|
||||||
|
let path = match unsafe { CStr::from_ptr(file_path).to_str() } {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => {
|
||||||
|
init_result = IdeviceLoggerError::InvalidPathString;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match File::create(path) {
|
||||||
|
Ok(f) => {
|
||||||
|
let (non_blocking, guard) = non_blocking(f);
|
||||||
|
file_guard = Some(guard);
|
||||||
|
let file_layer = fmt::layer()
|
||||||
|
.with_writer(non_blocking)
|
||||||
|
.with_ansi(false)
|
||||||
|
.with_target(false)
|
||||||
|
.with_filter(EnvFilter::new(file_filter));
|
||||||
|
layers.push(file_layer.boxed());
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
init_result = IdeviceLoggerError::FileError;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep guards alive (otherwise background threads die)
|
||||||
|
unsafe {
|
||||||
|
GUARDS = Some((console_guard, file_guard));
|
||||||
|
}
|
||||||
|
|
||||||
|
let subscriber = Registry::default().with(layers);
|
||||||
|
subscriber.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
if !INIT.is_completed() {
|
||||||
|
IdeviceLoggerError::AlreadyInitialized
|
||||||
|
} else {
|
||||||
|
init_result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use std::ptr::null_mut;
|
|||||||
|
|
||||||
use idevice::{IdeviceError, IdeviceService, misagent::MisagentClient, provider::IdeviceProvider};
|
use idevice::{IdeviceError, IdeviceService, misagent::MisagentClient, provider::IdeviceProvider};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, RUNTIME, ffi_err, provider::IdeviceProviderHandle};
|
use crate::{IdeviceFfiError, ffi_err, provider::IdeviceProviderHandle, run_sync_local};
|
||||||
|
|
||||||
pub struct MisagentClientHandle(pub MisagentClient);
|
pub struct MisagentClientHandle(pub MisagentClient);
|
||||||
|
|
||||||
@@ -28,11 +28,11 @@ pub unsafe extern "C" fn misagent_connect(
|
|||||||
client: *mut *mut MisagentClientHandle,
|
client: *mut *mut MisagentClientHandle,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if provider.is_null() || client.is_null() {
|
if provider.is_null() || client.is_null() {
|
||||||
log::error!("Null pointer provided");
|
tracing::error!("Null pointer provided");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<MisagentClient, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<MisagentClient, IdeviceError> = run_sync_local(async move {
|
||||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
MisagentClient::connect(provider_ref).await
|
MisagentClient::connect(provider_ref).await
|
||||||
});
|
});
|
||||||
@@ -72,7 +72,7 @@ pub unsafe extern "C" fn misagent_install(
|
|||||||
|
|
||||||
let profile = unsafe { std::slice::from_raw_parts(profile_data, profile_len) }.to_vec();
|
let profile = unsafe { std::slice::from_raw_parts(profile_data, profile_len) }.to_vec();
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async { unsafe { &mut *client }.0.install(profile).await });
|
let res = run_sync_local(async { unsafe { &mut *client }.0.install(profile).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
@@ -105,7 +105,7 @@ pub unsafe extern "C" fn misagent_remove(
|
|||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.into_owned();
|
.into_owned();
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async { unsafe { &mut *client }.0.remove(&id).await });
|
let res = run_sync_local(async { unsafe { &mut *client }.0.remove(&id).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => null_mut(),
|
Ok(_) => null_mut(),
|
||||||
@@ -143,7 +143,7 @@ pub unsafe extern "C" fn misagent_copy_all(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<Vec<Vec<u8>>, IdeviceError> =
|
let res: Result<Vec<Vec<u8>>, IdeviceError> =
|
||||||
RUNTIME.block_on(async { unsafe { &mut *client }.0.copy_all().await });
|
run_sync_local(async { unsafe { &mut *client }.0.copy_all().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(profiles) => {
|
Ok(profiles) => {
|
||||||
@@ -199,7 +199,7 @@ pub unsafe extern "C" fn misagent_free_profiles(
|
|||||||
|
|
||||||
for (ptr, len) in profiles.iter_mut().zip(lens.iter()) {
|
for (ptr, len) in profiles.iter_mut().zip(lens.iter()) {
|
||||||
if !ptr.is_null() {
|
if !ptr.is_null() {
|
||||||
let _ = unsafe { Box::from_raw(std::slice::from_raw_parts_mut(*ptr, *len)) };
|
let _ = unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(*ptr, *len)) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ pub unsafe extern "C" fn misagent_free_profiles(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn misagent_client_free(handle: *mut MisagentClientHandle) {
|
pub unsafe extern "C" fn misagent_client_free(handle: *mut MisagentClientHandle) {
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
log::debug!("Freeing misagent_client");
|
tracing::debug!("Freeing misagent_client");
|
||||||
let _ = unsafe { Box::from_raw(handle) };
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ use idevice::{
|
|||||||
use plist::Value;
|
use plist::Value;
|
||||||
use plist_ffi::{PlistWrapper, plist_t};
|
use plist_ffi::{PlistWrapper, plist_t};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, IdeviceHandle, RUNTIME, ffi_err, provider::IdeviceProviderHandle};
|
use crate::{
|
||||||
|
IdeviceFfiError, IdeviceHandle, ffi_err, provider::IdeviceProviderHandle, run_sync_local,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct ImageMounterHandle(pub ImageMounter);
|
pub struct ImageMounterHandle(pub ImageMounter);
|
||||||
|
|
||||||
@@ -30,11 +32,11 @@ pub unsafe extern "C" fn image_mounter_connect(
|
|||||||
client: *mut *mut ImageMounterHandle,
|
client: *mut *mut ImageMounterHandle,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if provider.is_null() || client.is_null() {
|
if provider.is_null() || client.is_null() {
|
||||||
log::error!("Null pointer provided");
|
tracing::error!("Null pointer provided");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<ImageMounter, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<ImageMounter, IdeviceError> = run_sync_local(async move {
|
||||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
ImageMounter::connect(provider_ref).await
|
ImageMounter::connect(provider_ref).await
|
||||||
});
|
});
|
||||||
@@ -90,7 +92,7 @@ pub unsafe extern "C" fn image_mounter_new(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn image_mounter_free(handle: *mut ImageMounterHandle) {
|
pub unsafe extern "C" fn image_mounter_free(handle: *mut ImageMounterHandle) {
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
log::debug!("Freeing image_mounter_client");
|
tracing::debug!("Freeing image_mounter_client");
|
||||||
let _ = unsafe { Box::from_raw(handle) };
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,7 +116,7 @@ pub unsafe extern "C" fn image_mounter_copy_devices(
|
|||||||
devices: *mut *mut plist_t,
|
devices: *mut *mut plist_t,
|
||||||
devices_len: *mut libc::size_t,
|
devices_len: *mut libc::size_t,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
let res: Result<Vec<Value>, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<Vec<Value>, IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.copy_devices().await
|
client_ref.copy_devices().await
|
||||||
});
|
});
|
||||||
@@ -171,7 +173,7 @@ pub unsafe extern "C" fn image_mounter_lookup_image(
|
|||||||
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<Vec<u8>, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<Vec<u8>, IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.lookup_image(image_type).await
|
client_ref.lookup_image(image_type).await
|
||||||
});
|
});
|
||||||
@@ -228,7 +230,7 @@ pub unsafe extern "C" fn image_mounter_upload_image(
|
|||||||
let image_slice = unsafe { std::slice::from_raw_parts(image, image_len) };
|
let image_slice = unsafe { std::slice::from_raw_parts(image, image_len) };
|
||||||
let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) };
|
let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) };
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref
|
client_ref
|
||||||
.upload_image(image_type, image_slice, signature_slice.to_vec())
|
.upload_image(image_type, image_slice, signature_slice.to_vec())
|
||||||
@@ -295,7 +297,7 @@ pub unsafe extern "C" fn image_mounter_mount_image(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref
|
client_ref
|
||||||
.mount_image(
|
.mount_image(
|
||||||
@@ -340,7 +342,7 @@ pub unsafe extern "C" fn image_mounter_unmount_image(
|
|||||||
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.unmount_image(mount_path).await
|
client_ref.unmount_image(mount_path).await
|
||||||
});
|
});
|
||||||
@@ -372,7 +374,7 @@ pub unsafe extern "C" fn image_mounter_query_developer_mode_status(
|
|||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<bool, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<bool, IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.query_developer_mode_status().await
|
client_ref.query_developer_mode_status().await
|
||||||
});
|
});
|
||||||
@@ -415,7 +417,7 @@ pub unsafe extern "C" fn image_mounter_mount_developer(
|
|||||||
let image_slice = unsafe { std::slice::from_raw_parts(image, image_len) };
|
let image_slice = unsafe { std::slice::from_raw_parts(image, image_len) };
|
||||||
let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) };
|
let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) };
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref
|
client_ref
|
||||||
.mount_developer(image_slice, signature_slice.to_vec())
|
.mount_developer(image_slice, signature_slice.to_vec())
|
||||||
@@ -465,7 +467,7 @@ pub unsafe extern "C" fn image_mounter_query_personalization_manifest(
|
|||||||
|
|
||||||
let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) };
|
let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) };
|
||||||
|
|
||||||
let res: Result<Vec<u8>, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<Vec<u8>, IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref
|
client_ref
|
||||||
.query_personalization_manifest(image_type, signature_slice.to_vec())
|
.query_personalization_manifest(image_type, signature_slice.to_vec())
|
||||||
@@ -521,7 +523,7 @@ pub unsafe extern "C" fn image_mounter_query_nonce(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<Vec<u8>, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<Vec<u8>, IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.query_nonce(image_type).await
|
client_ref.query_nonce(image_type).await
|
||||||
});
|
});
|
||||||
@@ -573,7 +575,7 @@ pub unsafe extern "C" fn image_mounter_query_personalization_identifiers(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<plist::Dictionary, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<plist::Dictionary, IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref
|
client_ref
|
||||||
.query_personalization_identifiers(image_type)
|
.query_personalization_identifiers(image_type)
|
||||||
@@ -604,7 +606,7 @@ pub unsafe extern "C" fn image_mounter_query_personalization_identifiers(
|
|||||||
pub unsafe extern "C" fn image_mounter_roll_personalization_nonce(
|
pub unsafe extern "C" fn image_mounter_roll_personalization_nonce(
|
||||||
client: *mut ImageMounterHandle,
|
client: *mut ImageMounterHandle,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.roll_personalization_nonce().await
|
client_ref.roll_personalization_nonce().await
|
||||||
});
|
});
|
||||||
@@ -629,7 +631,7 @@ pub unsafe extern "C" fn image_mounter_roll_personalization_nonce(
|
|||||||
pub unsafe extern "C" fn image_mounter_roll_cryptex_nonce(
|
pub unsafe extern "C" fn image_mounter_roll_cryptex_nonce(
|
||||||
client: *mut ImageMounterHandle,
|
client: *mut ImageMounterHandle,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
client_ref.roll_cryptex_nonce().await
|
client_ref.roll_cryptex_nonce().await
|
||||||
});
|
});
|
||||||
@@ -692,7 +694,7 @@ pub unsafe extern "C" fn image_mounter_mount_personalized(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
client_ref
|
client_ref
|
||||||
@@ -769,7 +771,7 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_with_callback(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<(), IdeviceError> = run_sync_local(async move {
|
||||||
let client_ref = unsafe { &mut (*client).0 };
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ use idevice::{
|
|||||||
IdeviceError, IdeviceService, os_trace_relay::OsTraceRelayClient, provider::IdeviceProvider,
|
IdeviceError, IdeviceService, os_trace_relay::OsTraceRelayClient, provider::IdeviceProvider,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, RUNTIME, ffi_err, provider::IdeviceProviderHandle};
|
use crate::run_sync_local;
|
||||||
|
use crate::{IdeviceFfiError, ffi_err, provider::IdeviceProviderHandle};
|
||||||
|
|
||||||
pub struct OsTraceRelayClientHandle(pub OsTraceRelayClient);
|
pub struct OsTraceRelayClientHandle(pub OsTraceRelayClient);
|
||||||
pub struct OsTraceRelayReceiverHandle(pub idevice::os_trace_relay::OsTraceRelayReceiver);
|
pub struct OsTraceRelayReceiverHandle(pub idevice::os_trace_relay::OsTraceRelayReceiver);
|
||||||
@@ -46,11 +47,11 @@ pub unsafe extern "C" fn os_trace_relay_connect(
|
|||||||
client: *mut *mut OsTraceRelayClientHandle,
|
client: *mut *mut OsTraceRelayClientHandle,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if provider.is_null() {
|
if provider.is_null() {
|
||||||
log::error!("Null pointer provided");
|
tracing::error!("Null pointer provided");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<OsTraceRelayClient, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<OsTraceRelayClient, IdeviceError> = run_sync_local(async move {
|
||||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
OsTraceRelayClient::connect(provider_ref).await
|
OsTraceRelayClient::connect(provider_ref).await
|
||||||
});
|
});
|
||||||
@@ -78,7 +79,7 @@ pub unsafe extern "C" fn os_trace_relay_connect(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn os_trace_relay_free(handle: *mut OsTraceRelayClientHandle) {
|
pub unsafe extern "C" fn os_trace_relay_free(handle: *mut OsTraceRelayClientHandle) {
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
log::debug!("Freeing os trace relay client");
|
tracing::debug!("Freeing os trace relay client");
|
||||||
let _ = unsafe { Box::from_raw(handle) };
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +103,7 @@ pub unsafe extern "C" fn os_trace_relay_start_trace(
|
|||||||
pid: *const u32,
|
pid: *const u32,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if receiver.is_null() || client.is_null() {
|
if receiver.is_null() || client.is_null() {
|
||||||
log::error!("Null pointer provided");
|
tracing::error!("Null pointer provided");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +115,7 @@ pub unsafe extern "C" fn os_trace_relay_start_trace(
|
|||||||
|
|
||||||
let client_owned = unsafe { Box::from_raw(client) };
|
let client_owned = unsafe { Box::from_raw(client) };
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async { client_owned.0.start_trace(pid_option).await });
|
let res = run_sync_local(async { client_owned.0.start_trace(pid_option).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(relay) => {
|
Ok(relay) => {
|
||||||
@@ -137,7 +138,7 @@ pub unsafe extern "C" fn os_trace_relay_start_trace(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn os_trace_relay_receiver_free(handle: *mut OsTraceRelayReceiverHandle) {
|
pub unsafe extern "C" fn os_trace_relay_receiver_free(handle: *mut OsTraceRelayReceiverHandle) {
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
log::debug!("Freeing syslog relay client");
|
tracing::debug!("Freeing syslog relay client");
|
||||||
let _ = unsafe { Box::from_raw(handle) };
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,7 +159,7 @@ pub unsafe extern "C" fn os_trace_relay_get_pid_list(
|
|||||||
client: *mut OsTraceRelayClientHandle,
|
client: *mut OsTraceRelayClientHandle,
|
||||||
list: *mut *mut Vec<u64>,
|
list: *mut *mut Vec<u64>,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
let res = RUNTIME.block_on(async { unsafe { &mut *client }.0.get_pid_list().await });
|
let res = run_sync_local(async { unsafe { &mut *client }.0.get_pid_list().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(r) => {
|
Ok(r) => {
|
||||||
@@ -186,11 +187,11 @@ pub unsafe extern "C" fn os_trace_relay_next(
|
|||||||
log: *mut *mut OsTraceLog,
|
log: *mut *mut OsTraceLog,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if client.is_null() {
|
if client.is_null() {
|
||||||
log::error!("Null pointer provided");
|
tracing::error!("Null pointer provided");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async { unsafe { &mut *client }.0.next().await });
|
let res = run_sync_local(async { unsafe { &mut *client }.0.next().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(r) => {
|
Ok(r) => {
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ pub unsafe extern "C" fn idevice_pairing_file_serialize(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn idevice_pairing_file_free(pairing_file: *mut IdevicePairingFile) {
|
pub unsafe extern "C" fn idevice_pairing_file_free(pairing_file: *mut IdevicePairingFile) {
|
||||||
if !pairing_file.is_null() {
|
if !pairing_file.is_null() {
|
||||||
log::debug!("Freeing pairing file");
|
tracing::debug!("Freeing pairing file");
|
||||||
let _ = unsafe { Box::from_raw(pairing_file) };
|
let _ = unsafe { Box::from_raw(pairing_file) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use std::{ffi::CStr, ptr::null_mut};
|
|||||||
|
|
||||||
use crate::util::{SockAddr, idevice_sockaddr};
|
use crate::util::{SockAddr, idevice_sockaddr};
|
||||||
use crate::{IdeviceFfiError, ffi_err, usbmuxd::UsbmuxdAddrHandle, util};
|
use crate::{IdeviceFfiError, ffi_err, usbmuxd::UsbmuxdAddrHandle, util};
|
||||||
use crate::{IdevicePairingFile, RUNTIME};
|
use crate::{IdevicePairingFile, run_sync};
|
||||||
|
|
||||||
pub struct IdeviceProviderHandle(pub Box<dyn IdeviceProvider>);
|
pub struct IdeviceProviderHandle(pub Box<dyn IdeviceProvider>);
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ pub unsafe extern "C" fn idevice_tcp_provider_new(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn idevice_provider_free(provider: *mut IdeviceProviderHandle) {
|
pub unsafe extern "C" fn idevice_provider_free(provider: *mut IdeviceProviderHandle) {
|
||||||
if !provider.is_null() {
|
if !provider.is_null() {
|
||||||
log::debug!("Freeing provider");
|
tracing::debug!("Freeing provider");
|
||||||
unsafe { drop(Box::from_raw(provider)) };
|
unsafe { drop(Box::from_raw(provider)) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,7 @@ pub unsafe extern "C" fn usbmuxd_provider_new(
|
|||||||
let udid = match unsafe { CStr::from_ptr(udid) }.to_str() {
|
let udid = match unsafe { CStr::from_ptr(udid) }.to_str() {
|
||||||
Ok(u) => u.to_string(),
|
Ok(u) => u.to_string(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Invalid UDID string: {e:?}");
|
tracing::error!("Invalid UDID string: {e:?}");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidString);
|
return ffi_err!(IdeviceError::FfiInvalidString);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -117,7 +117,7 @@ pub unsafe extern "C" fn usbmuxd_provider_new(
|
|||||||
let label = match unsafe { CStr::from_ptr(label) }.to_str() {
|
let label = match unsafe { CStr::from_ptr(label) }.to_str() {
|
||||||
Ok(l) => l.to_string(),
|
Ok(l) => l.to_string(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Invalid label string: {e:?}");
|
tracing::error!("Invalid label string: {e:?}");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -156,7 +156,7 @@ pub unsafe extern "C" fn idevice_provider_get_pairing_file(
|
|||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
let provider = unsafe { &mut *provider };
|
let provider = unsafe { &mut *provider };
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async move { provider.0.get_pairing_file().await });
|
let res = run_sync(async move { provider.0.get_pairing_file().await });
|
||||||
match res {
|
match res {
|
||||||
Ok(pf) => {
|
Ok(pf) => {
|
||||||
let pf = Box::new(IdevicePairingFile(pf));
|
let pf = Box::new(IdevicePairingFile(pf));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use std::ptr::{self, null_mut};
|
|||||||
|
|
||||||
use idevice::rsd::RsdHandshake;
|
use idevice::rsd::RsdHandshake;
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, RUNTIME, ReadWriteOpaque, ffi_err};
|
use crate::{IdeviceFfiError, ReadWriteOpaque, ffi_err, run_sync};
|
||||||
|
|
||||||
/// Opaque handle to an RsdHandshake
|
/// Opaque handle to an RsdHandshake
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -63,10 +63,10 @@ pub unsafe extern "C" fn rsd_handshake_new(
|
|||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let wrapper = unsafe { &mut *socket };
|
let mut wrapper = unsafe { Box::from_raw(socket) };
|
||||||
|
|
||||||
let res = match wrapper.inner.take() {
|
let res = match wrapper.inner.take() {
|
||||||
Some(mut w) => RUNTIME.block_on(async move { RsdHandshake::new(w.as_mut()).await }),
|
Some(mut w) => run_sync(async move { RsdHandshake::new(w.as_mut()).await }),
|
||||||
None => {
|
None => {
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|||||||
132
ffi/src/screenshotr.rs
Normal file
132
ffi/src/screenshotr.rs
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
|
||||||
|
use idevice::{
|
||||||
|
IdeviceError, IdeviceService, provider::IdeviceProvider,
|
||||||
|
services::screenshotr::ScreenshotService,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{IdeviceFfiError, ffi_err, provider::IdeviceProviderHandle, run_sync_local};
|
||||||
|
|
||||||
|
pub struct ScreenshotrClientHandle(pub ScreenshotService);
|
||||||
|
|
||||||
|
/// Represents a screenshot data buffer
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ScreenshotData {
|
||||||
|
pub data: *mut u8,
|
||||||
|
pub length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connects to screenshotr service using provider
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`provider`] - An IdeviceProvider
|
||||||
|
/// * [`client`] - On success, will be set to point to a newly allocated ScreenshotrClient handle
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `provider` must be a valid pointer to a handle allocated by this library
|
||||||
|
/// `client` must be a valid, non-null pointer to a location where the handle will be stored
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn screenshotr_connect(
|
||||||
|
provider: *mut IdeviceProviderHandle,
|
||||||
|
client: *mut *mut ScreenshotrClientHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if provider.is_null() || client.is_null() {
|
||||||
|
tracing::error!("Null pointer provided");
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res: Result<ScreenshotService, IdeviceError> = run_sync_local(async move {
|
||||||
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
|
ScreenshotService::connect(provider_ref).await
|
||||||
|
});
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(r) => {
|
||||||
|
let boxed = Box::new(ScreenshotrClientHandle(r));
|
||||||
|
unsafe { *client = Box::into_raw(boxed) };
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes a screenshot from the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid ScreenshotrClient handle
|
||||||
|
/// * `screenshot` - Pointer to store the screenshot data
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
/// `screenshot` must be a valid pointer to store the screenshot data
|
||||||
|
/// The caller is responsible for freeing the screenshot data using screenshotr_screenshot_free
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn screenshotr_take_screenshot(
|
||||||
|
client: *mut ScreenshotrClientHandle,
|
||||||
|
screenshot: *mut ScreenshotData,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() || screenshot.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res: Result<Vec<u8>, IdeviceError> = run_sync_local(async move {
|
||||||
|
let client_ref = unsafe { &mut (*client).0 };
|
||||||
|
client_ref.take_screenshot().await
|
||||||
|
});
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(data) => {
|
||||||
|
let len = data.len();
|
||||||
|
let boxed = data.into_boxed_slice();
|
||||||
|
let ptr = Box::into_raw(boxed) as *mut u8;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
(*screenshot).data = ptr;
|
||||||
|
(*screenshot).length = len;
|
||||||
|
}
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frees screenshot data
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `screenshot` - The screenshot data to free
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `screenshot` must be a valid ScreenshotData that was allocated by screenshotr_take_screenshot
|
||||||
|
/// or NULL (in which case this function does nothing)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn screenshotr_screenshot_free(screenshot: ScreenshotData) {
|
||||||
|
if !screenshot.data.is_null() && screenshot.length > 0 {
|
||||||
|
tracing::debug!("Freeing screenshot data");
|
||||||
|
let _ =
|
||||||
|
unsafe { Vec::from_raw_parts(screenshot.data, screenshot.length, screenshot.length) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frees a ScreenshotrClient handle
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * [`handle`] - The handle to free
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `handle` must be a valid pointer to the handle that was allocated by this library,
|
||||||
|
/// or NULL (in which case this function does nothing)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn screenshotr_client_free(handle: *mut ScreenshotrClientHandle) {
|
||||||
|
if !handle.is_null() {
|
||||||
|
tracing::debug!("Freeing screenshotr_client");
|
||||||
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,10 @@ use idevice::{
|
|||||||
springboardservices::SpringBoardServicesClient,
|
springboardservices::SpringBoardServicesClient,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, IdeviceHandle, RUNTIME, ffi_err, provider::IdeviceProviderHandle};
|
use crate::{
|
||||||
|
IdeviceFfiError, IdeviceHandle, ffi_err, provider::IdeviceProviderHandle, run_sync,
|
||||||
|
run_sync_local,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct SpringBoardServicesClientHandle(pub SpringBoardServicesClient);
|
pub struct SpringBoardServicesClientHandle(pub SpringBoardServicesClient);
|
||||||
|
|
||||||
@@ -30,11 +33,11 @@ pub unsafe extern "C" fn springboard_services_connect(
|
|||||||
client: *mut *mut SpringBoardServicesClientHandle,
|
client: *mut *mut SpringBoardServicesClientHandle,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if provider.is_null() || client.is_null() {
|
if provider.is_null() || client.is_null() {
|
||||||
log::error!("Null pointer provided");
|
tracing::error!("Null pointer provided");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<SpringBoardServicesClient, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<SpringBoardServicesClient, IdeviceError> = run_sync_local(async move {
|
||||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
SpringBoardServicesClient::connect(provider_ref).await
|
SpringBoardServicesClient::connect(provider_ref).await
|
||||||
});
|
});
|
||||||
@@ -103,7 +106,7 @@ pub unsafe extern "C" fn springboard_services_get_icon(
|
|||||||
out_result_len: *mut libc::size_t,
|
out_result_len: *mut libc::size_t,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if client.is_null() || out_result.is_null() || out_result_len.is_null() {
|
if client.is_null() || out_result.is_null() || out_result_len.is_null() {
|
||||||
log::error!("Invalid arguments: {client:?}, {out_result:?}");
|
tracing::error!("Invalid arguments: {client:?}, {out_result:?}");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
let client = unsafe { &mut *client };
|
let client = unsafe { &mut *client };
|
||||||
@@ -115,7 +118,7 @@ pub unsafe extern "C" fn springboard_services_get_icon(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<Vec<u8>, IdeviceError> =
|
let res: Result<Vec<u8>, IdeviceError> =
|
||||||
RUNTIME.block_on(async { client.0.get_icon_pngdata(bundle_id).await });
|
run_sync(async { client.0.get_icon_pngdata(bundle_id).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(r) => {
|
Ok(r) => {
|
||||||
@@ -134,6 +137,132 @@ pub unsafe extern "C" fn springboard_services_get_icon(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the home screen wallpaper preview as PNG image
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid SpringBoardServicesClient handle
|
||||||
|
/// * `out_result` - On success, will be set to point to newly allocated png image
|
||||||
|
/// * `out_result_len` - On success, will contain the size of the data in bytes
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
/// `out_result` and `out_result_len` must be valid, non-null pointers
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn springboard_services_get_home_screen_wallpaper_preview(
|
||||||
|
client: *mut SpringBoardServicesClientHandle,
|
||||||
|
out_result: *mut *mut c_void,
|
||||||
|
out_result_len: *mut libc::size_t,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() || out_result.is_null() || out_result_len.is_null() {
|
||||||
|
tracing::error!("Invalid arguments: {client:?}, {out_result:?}");
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
let client = unsafe { &mut *client };
|
||||||
|
|
||||||
|
let res: Result<Vec<u8>, IdeviceError> =
|
||||||
|
run_sync(async { client.0.get_home_screen_wallpaper_preview_pngdata().await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(r) => {
|
||||||
|
let len = r.len();
|
||||||
|
let boxed_slice = r.into_boxed_slice();
|
||||||
|
let ptr = boxed_slice.as_ptr();
|
||||||
|
std::mem::forget(boxed_slice);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*out_result = ptr as *mut c_void;
|
||||||
|
*out_result_len = len;
|
||||||
|
}
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the lock screen wallpaper preview as PNG image
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid SpringBoardServicesClient handle
|
||||||
|
/// * `out_result` - On success, will be set to point to newly allocated png image
|
||||||
|
/// * `out_result_len` - On success, will contain the size of the data in bytes
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
/// `out_result` and `out_result_len` must be valid, non-null pointers
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn springboard_services_get_lock_screen_wallpaper_preview(
|
||||||
|
client: *mut SpringBoardServicesClientHandle,
|
||||||
|
out_result: *mut *mut c_void,
|
||||||
|
out_result_len: *mut libc::size_t,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() || out_result.is_null() || out_result_len.is_null() {
|
||||||
|
tracing::error!("Invalid arguments: {client:?}, {out_result:?}");
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
let client = unsafe { &mut *client };
|
||||||
|
|
||||||
|
let res: Result<Vec<u8>, IdeviceError> =
|
||||||
|
run_sync(async { client.0.get_lock_screen_wallpaper_preview_pngdata().await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(r) => {
|
||||||
|
let len = r.len();
|
||||||
|
let boxed_slice = r.into_boxed_slice();
|
||||||
|
let ptr = boxed_slice.as_ptr();
|
||||||
|
std::mem::forget(boxed_slice);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*out_result = ptr as *mut c_void;
|
||||||
|
*out_result_len = len;
|
||||||
|
}
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the current interface orientation of the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `client` - A valid SpringBoardServicesClient handle
|
||||||
|
/// * `out_orientation` - On success, will contain the orientation value (0-4)
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An IdeviceFfiError on error, null on success
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `client` must be a valid pointer to a handle allocated by this library
|
||||||
|
/// `out_orientation` must be a valid, non-null pointer
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn springboard_services_get_interface_orientation(
|
||||||
|
client: *mut SpringBoardServicesClientHandle,
|
||||||
|
out_orientation: *mut u8,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if client.is_null() || out_orientation.is_null() {
|
||||||
|
tracing::error!("Invalid arguments: {client:?}, {out_orientation:?}");
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
let client = unsafe { &mut *client };
|
||||||
|
|
||||||
|
let res = run_sync(async { client.0.get_interface_orientation().await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(orientation) => {
|
||||||
|
unsafe {
|
||||||
|
*out_orientation = orientation as u8;
|
||||||
|
}
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Frees an SpringBoardServicesClient handle
|
/// Frees an SpringBoardServicesClient handle
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -145,7 +274,7 @@ pub unsafe extern "C" fn springboard_services_get_icon(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn springboard_services_free(handle: *mut SpringBoardServicesClientHandle) {
|
pub unsafe extern "C" fn springboard_services_free(handle: *mut SpringBoardServicesClientHandle) {
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
log::debug!("Freeing springboard_services_client");
|
tracing::debug!("Freeing springboard_services_client");
|
||||||
let _ = unsafe { Box::from_raw(handle) };
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use idevice::{
|
|||||||
IdeviceError, IdeviceService, provider::IdeviceProvider, syslog_relay::SyslogRelayClient,
|
IdeviceError, IdeviceService, provider::IdeviceProvider, syslog_relay::SyslogRelayClient,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, RUNTIME, ffi_err, provider::IdeviceProviderHandle};
|
use crate::{IdeviceFfiError, ffi_err, provider::IdeviceProviderHandle, run_sync_local};
|
||||||
|
|
||||||
pub struct SyslogRelayClientHandle(pub SyslogRelayClient);
|
pub struct SyslogRelayClientHandle(pub SyslogRelayClient);
|
||||||
|
|
||||||
@@ -23,11 +23,11 @@ pub unsafe extern "C" fn syslog_relay_connect_tcp(
|
|||||||
client: *mut *mut SyslogRelayClientHandle,
|
client: *mut *mut SyslogRelayClientHandle,
|
||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
if provider.is_null() {
|
if provider.is_null() {
|
||||||
log::error!("Null pointer provided");
|
tracing::error!("Null pointer provided");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res: Result<SyslogRelayClient, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<SyslogRelayClient, IdeviceError> = run_sync_local(async move {
|
||||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||||
SyslogRelayClient::connect(provider_ref).await
|
SyslogRelayClient::connect(provider_ref).await
|
||||||
});
|
});
|
||||||
@@ -58,7 +58,7 @@ pub unsafe extern "C" fn syslog_relay_connect_tcp(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn syslog_relay_client_free(handle: *mut SyslogRelayClientHandle) {
|
pub unsafe extern "C" fn syslog_relay_client_free(handle: *mut SyslogRelayClientHandle) {
|
||||||
if !handle.is_null() {
|
if !handle.is_null() {
|
||||||
log::debug!("Freeing syslog relay client");
|
tracing::debug!("Freeing syslog relay client");
|
||||||
let _ = unsafe { Box::from_raw(handle) };
|
let _ = unsafe { Box::from_raw(handle) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ pub unsafe extern "C" fn syslog_relay_next(
|
|||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async { unsafe { &mut *client }.0.next().await });
|
let res = run_sync_local(async { unsafe { &mut *client }.0.next().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(log) => {
|
Ok(log) => {
|
||||||
@@ -96,7 +96,7 @@ pub unsafe extern "C" fn syslog_relay_next(
|
|||||||
null_mut()
|
null_mut()
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
log::error!("Failed to convert log message to C string");
|
tracing::error!("Failed to convert log message to C string");
|
||||||
ffi_err!(IdeviceError::FfiInvalidString)
|
ffi_err!(IdeviceError::FfiInvalidString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use tokio::{
|
|||||||
net::tcp::{OwnedReadHalf, OwnedWriteHalf},
|
net::tcp::{OwnedReadHalf, OwnedWriteHalf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, RUNTIME, core_device_proxy::AdapterHandle, ffi_err};
|
use crate::{IdeviceFfiError, core_device_proxy::AdapterHandle, ffi_err, run_sync, run_sync_local};
|
||||||
|
|
||||||
pub struct TcpFeedObject {
|
pub struct TcpFeedObject {
|
||||||
sender: Arc<Mutex<OwnedWriteHalf>>,
|
sender: Arc<Mutex<OwnedWriteHalf>>,
|
||||||
@@ -63,7 +63,7 @@ pub unsafe extern "C" fn idevice_tcp_stack_into_sync_objects(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async {
|
let res = run_sync(async {
|
||||||
let mut port = 4000;
|
let mut port = 4000;
|
||||||
loop {
|
loop {
|
||||||
if port > 4050 {
|
if port > 4050 {
|
||||||
@@ -105,7 +105,7 @@ pub unsafe extern "C" fn idevice_tcp_stack_into_sync_objects(
|
|||||||
let eat_object = TcpEatObject { receiver: r };
|
let eat_object = TcpEatObject { receiver: r };
|
||||||
|
|
||||||
// we must be inside the runtime for the inner function to spawn threads
|
// we must be inside the runtime for the inner function to spawn threads
|
||||||
let new_adapter = RUNTIME.block_on(async {
|
let new_adapter = run_sync_local(async {
|
||||||
idevice::tcp::adapter::Adapter::new(Box::new(stream), our_ip, their_ip).to_async_handle()
|
idevice::tcp::adapter::Adapter::new(Box::new(stream), our_ip, their_ip).to_async_handle()
|
||||||
});
|
});
|
||||||
// this object can now be used with the rest of the idevice FFI library
|
// this object can now be used with the rest of the idevice FFI library
|
||||||
@@ -133,7 +133,7 @@ pub unsafe extern "C" fn idevice_tcp_feed_object_write(
|
|||||||
}
|
}
|
||||||
let object = unsafe { &mut *object };
|
let object = unsafe { &mut *object };
|
||||||
let data = unsafe { std::slice::from_raw_parts(data, len) };
|
let data = unsafe { std::slice::from_raw_parts(data, len) };
|
||||||
RUNTIME.block_on(async move {
|
run_sync_local(async move {
|
||||||
let mut lock = object.sender.lock().await;
|
let mut lock = object.sender.lock().await;
|
||||||
match lock.write_all(data).await {
|
match lock.write_all(data).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
@@ -163,7 +163,7 @@ pub unsafe extern "C" fn idevice_tcp_eat_object_read(
|
|||||||
) -> *mut IdeviceFfiError {
|
) -> *mut IdeviceFfiError {
|
||||||
let object = unsafe { &mut *object };
|
let object = unsafe { &mut *object };
|
||||||
let mut buf = [0; 2048];
|
let mut buf = [0; 2048];
|
||||||
RUNTIME.block_on(async {
|
run_sync_local(async {
|
||||||
let lock = object.receiver.lock().await;
|
let lock = object.receiver.lock().await;
|
||||||
match lock.try_read(&mut buf) {
|
match lock.try_read(&mut buf) {
|
||||||
Ok(size) => {
|
Ok(size) => {
|
||||||
|
|||||||
@@ -2,22 +2,27 @@
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
ffi::{CStr, CString, c_char},
|
ffi::{CStr, CString, c_char},
|
||||||
|
pin::Pin,
|
||||||
ptr::null_mut,
|
ptr::null_mut,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
IdeviceFfiError, IdeviceHandle, IdevicePairingFile, RUNTIME, ffi_err,
|
IdeviceFfiError, IdeviceHandle, IdevicePairingFile, ffi_err, run_sync, run_sync_local,
|
||||||
util::{SockAddr, c_socket_to_rust, idevice_sockaddr, idevice_socklen_t},
|
util::{SockAddr, c_socket_to_rust, idevice_sockaddr, idevice_socklen_t},
|
||||||
};
|
};
|
||||||
|
use futures::{Stream, StreamExt};
|
||||||
use idevice::{
|
use idevice::{
|
||||||
IdeviceError,
|
IdeviceError,
|
||||||
usbmuxd::{UsbmuxdAddr, UsbmuxdConnection, UsbmuxdDevice},
|
usbmuxd::{UsbmuxdAddr, UsbmuxdConnection, UsbmuxdDevice, UsbmuxdListenEvent},
|
||||||
};
|
};
|
||||||
use log::error;
|
use tracing::error;
|
||||||
|
|
||||||
pub struct UsbmuxdConnectionHandle(pub UsbmuxdConnection);
|
pub struct UsbmuxdConnectionHandle(pub UsbmuxdConnection);
|
||||||
pub struct UsbmuxdAddrHandle(pub UsbmuxdAddr);
|
pub struct UsbmuxdAddrHandle(pub UsbmuxdAddr);
|
||||||
pub struct UsbmuxdDeviceHandle(pub UsbmuxdDevice);
|
pub struct UsbmuxdDeviceHandle(pub UsbmuxdDevice);
|
||||||
|
pub struct UsbmuxdListenerHandle<'a>(
|
||||||
|
Pin<Box<dyn Stream<Item = Result<UsbmuxdListenEvent, IdeviceError>> + 'a>>,
|
||||||
|
);
|
||||||
|
|
||||||
/// Connects to a usbmuxd instance over TCP
|
/// Connects to a usbmuxd instance over TCP
|
||||||
///
|
///
|
||||||
@@ -52,7 +57,7 @@ pub unsafe extern "C" fn idevice_usbmuxd_new_tcp_connection(
|
|||||||
Err(e) => return ffi_err!(e),
|
Err(e) => return ffi_err!(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async move {
|
let res = run_sync(async move {
|
||||||
let stream = tokio::net::TcpStream::connect(addr).await?;
|
let stream = tokio::net::TcpStream::connect(addr).await?;
|
||||||
Ok::<_, IdeviceError>(UsbmuxdConnection::new(Box::new(stream), tag))
|
Ok::<_, IdeviceError>(UsbmuxdConnection::new(Box::new(stream), tag))
|
||||||
});
|
});
|
||||||
@@ -91,7 +96,7 @@ pub unsafe extern "C" fn idevice_usbmuxd_new_unix_socket_connection(
|
|||||||
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<UsbmuxdConnection, IdeviceError> = RUNTIME.block_on(async move {
|
let res: Result<UsbmuxdConnection, IdeviceError> = run_sync(async move {
|
||||||
let stream = tokio::net::UnixStream::connect(addr).await?;
|
let stream = tokio::net::UnixStream::connect(addr).await?;
|
||||||
Ok(UsbmuxdConnection::new(Box::new(stream), tag))
|
Ok(UsbmuxdConnection::new(Box::new(stream), tag))
|
||||||
});
|
});
|
||||||
@@ -127,13 +132,13 @@ pub unsafe extern "C" fn idevice_usbmuxd_new_default_connection(
|
|||||||
let addr = match UsbmuxdAddr::from_env_var() {
|
let addr = match UsbmuxdAddr::from_env_var() {
|
||||||
Ok(a) => a,
|
Ok(a) => a,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Invalid address set: {e:?}");
|
tracing::error!("Invalid address set: {e:?}");
|
||||||
return ffi_err!(IdeviceError::FfiInvalidArg);
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let res: Result<UsbmuxdConnection, IdeviceError> =
|
let res: Result<UsbmuxdConnection, IdeviceError> =
|
||||||
RUNTIME.block_on(async move { addr.connect(tag).await });
|
run_sync(async move { addr.connect(tag).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(r) => {
|
Ok(r) => {
|
||||||
@@ -171,7 +176,7 @@ pub unsafe extern "C" fn idevice_usbmuxd_get_devices(
|
|||||||
}
|
}
|
||||||
let conn = unsafe { &mut (*usbmuxd_conn).0 };
|
let conn = unsafe { &mut (*usbmuxd_conn).0 };
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async { conn.get_devices().await });
|
let res = run_sync(async { conn.get_devices().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(device_vec) => {
|
Ok(device_vec) => {
|
||||||
@@ -239,7 +244,7 @@ pub unsafe extern "C" fn idevice_usbmuxd_connect_to_device(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async move { conn.connect_to_device(device_id, port, label).await });
|
let res = run_sync(async move { conn.connect_to_device(device_id, port, label).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(device_conn) => {
|
Ok(device_conn) => {
|
||||||
@@ -287,7 +292,7 @@ pub unsafe extern "C" fn idevice_usbmuxd_get_pair_record(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async { conn.get_pair_record(udid_str).await });
|
let res = run_sync(async { conn.get_pair_record(udid_str).await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(pf) => {
|
Ok(pf) => {
|
||||||
@@ -301,6 +306,149 @@ pub unsafe extern "C" fn idevice_usbmuxd_get_pair_record(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Saves the pairing record for a given device UDID.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `usbmuxd_conn` - A valid connection to usbmuxd.
|
||||||
|
/// * `device_id` - The muxer ID for the device
|
||||||
|
/// * `udid` - The UDID of the device.
|
||||||
|
/// * `pair_record` - The bytes of the pairing record plist to save
|
||||||
|
/// * `pair_record_len` - the length of the pairing record bytes
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An `IdeviceFfiError` on error, `null` on success.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// * `usbmuxd_conn` must be a valid pointer.
|
||||||
|
/// * `udid` must be a valid, null-terminated C string.
|
||||||
|
/// * `pair_record` must be a valid, non-null pointer.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_usbmuxd_save_pair_record(
|
||||||
|
usbmuxd_conn: *mut UsbmuxdConnectionHandle,
|
||||||
|
udid: *const c_char,
|
||||||
|
pair_record: *mut u8,
|
||||||
|
pair_record_len: usize,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if usbmuxd_conn.is_null() || pair_record.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
let conn = unsafe { &mut (*usbmuxd_conn).0 };
|
||||||
|
let pair_record =
|
||||||
|
unsafe { std::slice::from_raw_parts_mut(pair_record, pair_record_len) }.to_vec();
|
||||||
|
|
||||||
|
let udid_str = unsafe {
|
||||||
|
match CStr::from_ptr(udid).to_str() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = run_sync_local(async { conn.save_pair_record(udid_str, pair_record).await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(_) => null_mut(),
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Listens on the socket for connections and disconnections
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Pass valid pointers. Free the stream with ``idevice_usbmuxd_listener_handle_free``.
|
||||||
|
/// The stream must outlive the usbmuxd connection, and the usbmuxd connection cannot
|
||||||
|
/// be used for other requests.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_usbmuxd_listen(
|
||||||
|
usbmuxd_conn: *mut UsbmuxdConnectionHandle,
|
||||||
|
stream_handle: *mut *mut UsbmuxdListenerHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if usbmuxd_conn.is_null() || stream_handle.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
let conn = unsafe { &mut (*usbmuxd_conn).0 };
|
||||||
|
|
||||||
|
let res = run_sync_local(async { conn.listen().await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(s) => {
|
||||||
|
unsafe { *stream_handle = Box::into_raw(Box::new(UsbmuxdListenerHandle(s))) };
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frees a stream created by ``listen`` or does nothing on null
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Pass a valid pointer.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_usbmuxd_listener_handle_free(
|
||||||
|
stream_handle: *mut UsbmuxdListenerHandle,
|
||||||
|
) {
|
||||||
|
if stream_handle.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let _ = unsafe { Box::from_raw(stream_handle) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the next event from the stream.
|
||||||
|
/// Connect will be set to true if the event is a connection event,
|
||||||
|
/// and the connection_device will be filled with the device information.
|
||||||
|
/// If connection is false, the mux ID of the device will be filled.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `stream_handle` - The handle to the stream returned by listen
|
||||||
|
/// * `connect` - The bool that will be set
|
||||||
|
/// * `connection_device` - The pointer that will be filled on a connect event
|
||||||
|
/// * `disconnection_id` - The mux ID that will be set on a disconnect event
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Pass valid pointers
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_usbmuxd_listener_next(
|
||||||
|
stream_handle: *mut UsbmuxdListenerHandle,
|
||||||
|
connect: *mut bool,
|
||||||
|
connection_device: *mut *mut UsbmuxdDeviceHandle,
|
||||||
|
disconnection_id: *mut u32,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if stream_handle.is_null()
|
||||||
|
|| connect.is_null()
|
||||||
|
|| connection_device.is_null()
|
||||||
|
|| disconnection_id.is_null()
|
||||||
|
{
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
let stream = unsafe { &mut (*stream_handle).0 };
|
||||||
|
|
||||||
|
let res = run_sync_local(async { stream.next().await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Some(res) => match res {
|
||||||
|
Ok(s) => {
|
||||||
|
match s {
|
||||||
|
UsbmuxdListenEvent::Connected(usbmuxd_device) => {
|
||||||
|
unsafe { *connect = true };
|
||||||
|
unsafe {
|
||||||
|
*connection_device =
|
||||||
|
Box::into_raw(Box::new(UsbmuxdDeviceHandle(usbmuxd_device)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
UsbmuxdListenEvent::Disconnected(id) => unsafe { *disconnection_id = id },
|
||||||
|
}
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
ffi_err!(IdeviceError::Socket(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::BrokenPipe,
|
||||||
|
"end of stream"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads the BUID (Boot-Unique ID) from usbmuxd.
|
/// Reads the BUID (Boot-Unique ID) from usbmuxd.
|
||||||
///
|
///
|
||||||
/// The returned string must be freed with `idevice_string_free`.
|
/// The returned string must be freed with `idevice_string_free`.
|
||||||
@@ -325,7 +473,7 @@ pub unsafe extern "C" fn idevice_usbmuxd_get_buid(
|
|||||||
}
|
}
|
||||||
let conn = unsafe { &mut (*usbmuxd_conn).0 };
|
let conn = unsafe { &mut (*usbmuxd_conn).0 };
|
||||||
|
|
||||||
let res = RUNTIME.block_on(async { conn.get_buid().await });
|
let res = run_sync(async { conn.get_buid().await });
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(buid_str) => match CString::new(buid_str) {
|
Ok(buid_str) => match CString::new(buid_str) {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ pub(crate) fn c_socket_to_rust(
|
|||||||
addr_len: SockLen,
|
addr_len: SockLen,
|
||||||
) -> Result<SocketAddr, IdeviceError> {
|
) -> Result<SocketAddr, IdeviceError> {
|
||||||
if addr.is_null() {
|
if addr.is_null() {
|
||||||
log::error!("null sockaddr");
|
tracing::error!("null sockaddr");
|
||||||
return invalid_arg();
|
return invalid_arg();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ pub(crate) fn c_socket_to_rust(
|
|||||||
match family as i32 {
|
match family as i32 {
|
||||||
libc::AF_INET => {
|
libc::AF_INET => {
|
||||||
if (addr_len as usize) < std::mem::size_of::<sockaddr_in>() {
|
if (addr_len as usize) < std::mem::size_of::<sockaddr_in>() {
|
||||||
log::error!("Invalid sockaddr_in size");
|
tracing::error!("Invalid sockaddr_in size");
|
||||||
return invalid_arg();
|
return invalid_arg();
|
||||||
}
|
}
|
||||||
let a = &*(addr as *const sockaddr_in);
|
let a = &*(addr as *const sockaddr_in);
|
||||||
@@ -69,7 +69,7 @@ pub(crate) fn c_socket_to_rust(
|
|||||||
}
|
}
|
||||||
libc::AF_INET6 => {
|
libc::AF_INET6 => {
|
||||||
if (addr_len as usize) < std::mem::size_of::<sockaddr_in6>() {
|
if (addr_len as usize) < std::mem::size_of::<sockaddr_in6>() {
|
||||||
log::error!("Invalid sockaddr_in6 size");
|
tracing::error!("Invalid sockaddr_in6 size");
|
||||||
return invalid_arg();
|
return invalid_arg();
|
||||||
}
|
}
|
||||||
let a = &*(addr as *const sockaddr_in6);
|
let a = &*(addr as *const sockaddr_in6);
|
||||||
@@ -83,7 +83,7 @@ pub(crate) fn c_socket_to_rust(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::error!(
|
tracing::error!(
|
||||||
"Unsupported socket address family: {}",
|
"Unsupported socket address family: {}",
|
||||||
(*addr).sa_family as i32
|
(*addr).sa_family as i32
|
||||||
);
|
);
|
||||||
@@ -95,7 +95,7 @@ pub(crate) fn c_socket_to_rust(
|
|||||||
match family {
|
match family {
|
||||||
AF_INET => {
|
AF_INET => {
|
||||||
if (addr_len as usize) < std::mem::size_of::<sockaddr_in>() {
|
if (addr_len as usize) < std::mem::size_of::<sockaddr_in>() {
|
||||||
log::error!("Invalid SOCKADDR_IN size");
|
tracing::error!("Invalid SOCKADDR_IN size");
|
||||||
return invalid_arg();
|
return invalid_arg();
|
||||||
}
|
}
|
||||||
let a = &*(addr as *const sockaddr_in);
|
let a = &*(addr as *const sockaddr_in);
|
||||||
@@ -107,7 +107,7 @@ pub(crate) fn c_socket_to_rust(
|
|||||||
}
|
}
|
||||||
AF_INET6 => {
|
AF_INET6 => {
|
||||||
if (addr_len as usize) < std::mem::size_of::<sockaddr_in6>() {
|
if (addr_len as usize) < std::mem::size_of::<sockaddr_in6>() {
|
||||||
log::error!("Invalid SOCKADDR_IN6 size");
|
tracing::error!("Invalid SOCKADDR_IN6 size");
|
||||||
return invalid_arg();
|
return invalid_arg();
|
||||||
}
|
}
|
||||||
let a = &*(addr as *const sockaddr_in6);
|
let a = &*(addr as *const sockaddr_in6);
|
||||||
@@ -124,7 +124,7 @@ pub(crate) fn c_socket_to_rust(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Unsupported socket address family: {}", (*addr).sa_family);
|
tracing::error!("Unsupported socket address family: {}", (*addr).sa_family);
|
||||||
invalid_arg()
|
invalid_arg()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,7 +133,7 @@ pub(crate) fn c_socket_to_rust(
|
|||||||
|
|
||||||
pub(crate) fn c_addr_to_rust(addr: *const SockAddr) -> Result<IpAddr, IdeviceError> {
|
pub(crate) fn c_addr_to_rust(addr: *const SockAddr) -> Result<IpAddr, IdeviceError> {
|
||||||
if addr.is_null() {
|
if addr.is_null() {
|
||||||
log::error!("null sockaddr");
|
tracing::error!("null sockaddr");
|
||||||
return invalid_arg();
|
return invalid_arg();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ pub(crate) fn c_addr_to_rust(addr: *const SockAddr) -> Result<IpAddr, IdeviceErr
|
|||||||
Ok(IpAddr::V6(Ipv6Addr::from(a.sin6_addr.s6_addr)))
|
Ok(IpAddr::V6(Ipv6Addr::from(a.sin6_addr.s6_addr)))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::error!(
|
tracing::error!(
|
||||||
"Unsupported socket address family: {}",
|
"Unsupported socket address family: {}",
|
||||||
(*addr).sa_family as i32
|
(*addr).sa_family as i32
|
||||||
);
|
);
|
||||||
@@ -178,7 +178,7 @@ pub(crate) fn c_addr_to_rust(addr: *const SockAddr) -> Result<IpAddr, IdeviceErr
|
|||||||
Ok(IpAddr::V6(Ipv6Addr::from(bytes)))
|
Ok(IpAddr::V6(Ipv6Addr::from(bytes)))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Unsupported socket address family: {}", (*addr).sa_family);
|
tracing::error!("Unsupported socket address family: {}", (*addr).sa_family);
|
||||||
invalid_arg()
|
invalid_arg()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name = "idevice"
|
name = "idevice"
|
||||||
description = "A Rust library to interact with services on iOS devices."
|
description = "A Rust library to interact with services on iOS devices."
|
||||||
authors = ["Jackson Coxson"]
|
authors = ["Jackson Coxson"]
|
||||||
version = "0.1.42"
|
version = "0.1.53"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
documentation = "https://docs.rs/idevice"
|
documentation = "https://docs.rs/idevice"
|
||||||
@@ -11,26 +11,28 @@ keywords = ["lockdownd", "ios"]
|
|||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.43", features = ["io-util"] }
|
tokio = { version = "1", features = ["io-util"] }
|
||||||
tokio-rustls = { version = "0.26", default-features = false }
|
tokio-rustls = { version = "0.26", default-features = false, optional = true }
|
||||||
rustls = { version = "0.23", default-features = false, features = [
|
rustls = { version = "0.23", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"tls12",
|
"tls12",
|
||||||
] }
|
], optional = true }
|
||||||
crossfire = { version = "2.0", optional = true } # TODO: update to 2.1 when it comes out
|
tokio-openssl = { version = "0.6", optional = true }
|
||||||
|
openssl = { version = "0.10", optional = true }
|
||||||
|
|
||||||
plist = { version = "1.7" }
|
plist = { version = "1.8" }
|
||||||
|
plist-macro = { version = "0.1.3" }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
ns-keyed-archive = { version = "0.1.3", optional = true }
|
ns-keyed-archive = { version = "0.1.4", optional = true }
|
||||||
|
crossfire = { version = "2.1", optional = true }
|
||||||
|
|
||||||
thiserror = { version = "2" }
|
thiserror = { version = "2" }
|
||||||
log = { version = "0.4" }
|
tracing = { version = "0.1.41" }
|
||||||
env_logger = { version = "0.11" }
|
|
||||||
base64 = { version = "0.22" }
|
base64 = { version = "0.22" }
|
||||||
|
|
||||||
indexmap = { version = "2.7", features = ["serde"], optional = true }
|
indexmap = { version = "2.11", features = ["serde"], optional = true }
|
||||||
uuid = { version = "1.12", features = ["serde", "v4"], optional = true }
|
uuid = { version = "1.18", features = ["serde", "v4"], optional = true }
|
||||||
chrono = { version = "0.4.40", optional = true, default-features = false, features = [
|
chrono = { version = "0.4", optional = true, default-features = false, features = [
|
||||||
"serde",
|
"serde",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
@@ -39,7 +41,7 @@ json = { version = "0.12", optional = true }
|
|||||||
byteorder = { version = "1.5", optional = true }
|
byteorder = { version = "1.5", optional = true }
|
||||||
bytes = { version = "1.10", optional = true }
|
bytes = { version = "1.10", optional = true }
|
||||||
|
|
||||||
reqwest = { version = "0.12", features = [
|
reqwest = { version = "0.13", features = [
|
||||||
"json",
|
"json",
|
||||||
], optional = true, default-features = false }
|
], optional = true, default-features = false }
|
||||||
rand = { version = "0.9", optional = true }
|
rand = { version = "0.9", optional = true }
|
||||||
@@ -53,10 +55,6 @@ x509-cert = { version = "0.2", optional = true, features = [
|
|||||||
"builder",
|
"builder",
|
||||||
"pem",
|
"pem",
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
x25519-dalek = { version = "2", optional = true }
|
|
||||||
ed25519-dalek = { version = "2", features = ["rand_core"], optional = true }
|
|
||||||
hkdf = { version = "0.12", optional = true }
|
|
||||||
chacha20poly1305 = { version = "0.10", optional = true }
|
|
||||||
|
|
||||||
obfstr = { version = "0.4", optional = true }
|
obfstr = { version = "0.4", optional = true }
|
||||||
|
|
||||||
@@ -69,14 +67,16 @@ bytes = "1.10.1"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["aws-lc"]
|
default = ["aws-lc"]
|
||||||
aws-lc = ["rustls/aws-lc-rs", "tokio-rustls/aws-lc-rs"]
|
aws-lc = ["rustls", "rustls/aws-lc-rs", "tokio-rustls/aws-lc-rs"]
|
||||||
ring = ["rustls/ring", "tokio-rustls/ring"]
|
ring = ["rustls", "rustls/ring", "tokio-rustls/ring"]
|
||||||
|
rustls = ["dep:rustls", "dep:tokio-rustls"]
|
||||||
|
openssl = ["dep:openssl", "dep:tokio-openssl"]
|
||||||
|
|
||||||
afc = ["dep:chrono"]
|
afc = ["dep:chrono", "dep:futures"]
|
||||||
amfi = []
|
amfi = []
|
||||||
bt_packet_logger = []
|
bt_packet_logger = []
|
||||||
companion_proxy = []
|
companion_proxy = []
|
||||||
core_device = ["xpc", "dep:uuid"]
|
core_device = ["xpc", "dep:uuid", "dep:ns-keyed-archive"]
|
||||||
core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"]
|
core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"]
|
||||||
crashreportcopymobile = ["afc"]
|
crashreportcopymobile = ["afc"]
|
||||||
debug_proxy = []
|
debug_proxy = []
|
||||||
@@ -91,6 +91,7 @@ installation_proxy = [
|
|||||||
"async_zip/deflate",
|
"async_zip/deflate",
|
||||||
"tokio/fs",
|
"tokio/fs",
|
||||||
]
|
]
|
||||||
|
installcoordination_proxy = []
|
||||||
springboardservices = []
|
springboardservices = []
|
||||||
misagent = []
|
misagent = []
|
||||||
mobile_image_mounter = ["dep:sha2"]
|
mobile_image_mounter = ["dep:sha2"]
|
||||||
@@ -102,14 +103,6 @@ pcapd = []
|
|||||||
preboard_service = []
|
preboard_service = []
|
||||||
obfuscate = ["dep:obfstr"]
|
obfuscate = ["dep:obfstr"]
|
||||||
restore_service = []
|
restore_service = []
|
||||||
remote_pairing = [
|
|
||||||
"dep:json",
|
|
||||||
"dep:x25519-dalek",
|
|
||||||
"dep:ed25519-dalek",
|
|
||||||
"dep:hkdf",
|
|
||||||
"dep:chacha20poly1305",
|
|
||||||
"dep:uuid",
|
|
||||||
]
|
|
||||||
rsd = ["xpc"]
|
rsd = ["xpc"]
|
||||||
screenshotr = []
|
screenshotr = []
|
||||||
syslog_relay = ["dep:bytes"]
|
syslog_relay = ["dep:bytes"]
|
||||||
@@ -123,7 +116,7 @@ tunnel_tcp_stack = [
|
|||||||
]
|
]
|
||||||
tss = ["dep:uuid", "dep:reqwest"]
|
tss = ["dep:uuid", "dep:reqwest"]
|
||||||
tunneld = ["dep:serde_json", "dep:json", "dep:reqwest"]
|
tunneld = ["dep:serde_json", "dep:json", "dep:reqwest"]
|
||||||
usbmuxd = ["tokio/net"]
|
usbmuxd = ["tokio/net", "dep:futures"]
|
||||||
xpc = ["dep:indexmap", "dep:uuid", "dep:async-stream"]
|
xpc = ["dep:indexmap", "dep:uuid", "dep:async-stream"]
|
||||||
full = [
|
full = [
|
||||||
"afc",
|
"afc",
|
||||||
@@ -139,6 +132,7 @@ full = [
|
|||||||
"heartbeat",
|
"heartbeat",
|
||||||
"house_arrest",
|
"house_arrest",
|
||||||
"installation_proxy",
|
"installation_proxy",
|
||||||
|
"installcoordination_proxy",
|
||||||
"location_simulation",
|
"location_simulation",
|
||||||
"misagent",
|
"misagent",
|
||||||
"mobile_image_mounter",
|
"mobile_image_mounter",
|
||||||
@@ -148,10 +142,6 @@ full = [
|
|||||||
"pcapd",
|
"pcapd",
|
||||||
"preboard_service",
|
"preboard_service",
|
||||||
"restore_service",
|
"restore_service",
|
||||||
"usbmuxd",
|
|
||||||
"xpc",
|
|
||||||
"location_simulation",
|
|
||||||
"remote_pairing",
|
|
||||||
"rsd",
|
"rsd",
|
||||||
"screenshotr",
|
"screenshotr",
|
||||||
"springboardservices",
|
"springboardservices",
|
||||||
|
|||||||
334
idevice/src/cursor.rs
Normal file
334
idevice/src/cursor.rs
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Cursor<'a> {
|
||||||
|
inner: &'a [u8],
|
||||||
|
pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Cursor<'a> {
|
||||||
|
/// Creates a new cursor
|
||||||
|
pub fn new(inner: &'a [u8]) -> Self {
|
||||||
|
Self { inner, pos: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.inner.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.inner.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn at_end(&self) -> bool {
|
||||||
|
self.pos == self.inner.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self, to_read: usize) -> Option<&'a [u8]> {
|
||||||
|
// Check if the end of the slice (self.pos + to_read) is beyond the buffer length
|
||||||
|
if self
|
||||||
|
.pos
|
||||||
|
.checked_add(to_read)
|
||||||
|
.is_none_or(|end_pos| end_pos > self.inner.len())
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The end of the slice is self.pos + to_read
|
||||||
|
let end_pos = self.pos + to_read;
|
||||||
|
let res = Some(&self.inner[self.pos..end_pos]);
|
||||||
|
self.pos = end_pos;
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn back(&mut self, to_back: usize) {
|
||||||
|
let to_back = if to_back > self.pos {
|
||||||
|
self.pos
|
||||||
|
} else {
|
||||||
|
to_back
|
||||||
|
};
|
||||||
|
|
||||||
|
self.pos -= to_back;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True if actually all zeroes
|
||||||
|
pub fn read_assert_zero(&mut self, to_read: usize) -> Option<()> {
|
||||||
|
let bytes = self.read(to_read)?;
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
for b in bytes.iter() {
|
||||||
|
if *b > 0 {
|
||||||
|
eprintln!("Zero read contained non-zero values!");
|
||||||
|
eprintln!("{bytes:02X?}");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_to(&mut self, end: usize) -> Option<&'a [u8]> {
|
||||||
|
if end > self.inner.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let res = Some(&self.inner[self.pos..end]);
|
||||||
|
self.pos = end;
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek_to(&mut self, end: usize) -> Option<&'a [u8]> {
|
||||||
|
if end > self.inner.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(&self.inner[self.pos..end])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek(&self, to_read: usize) -> Option<&'a [u8]> {
|
||||||
|
if self
|
||||||
|
.pos
|
||||||
|
.checked_add(to_read)
|
||||||
|
.is_none_or(|end_pos| end_pos > self.inner.len())
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let end_pos = self.pos + to_read;
|
||||||
|
Some(&self.inner[self.pos..end_pos])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reveal(&self, surrounding: usize) {
|
||||||
|
let len = self.inner.len();
|
||||||
|
|
||||||
|
if self.pos > len {
|
||||||
|
println!("Cursor is past end of buffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = self.pos.saturating_sub(surrounding);
|
||||||
|
let end = (self.pos + surrounding + 1).min(len);
|
||||||
|
|
||||||
|
// HEADER
|
||||||
|
println!("Reveal around pos {} ({} bytes):", self.pos, surrounding);
|
||||||
|
|
||||||
|
// --- HEX LINE ---
|
||||||
|
print!("Hex: ");
|
||||||
|
for i in start..end {
|
||||||
|
if i == self.pos {
|
||||||
|
print!("[{:02X}] ", self.inner[i]);
|
||||||
|
} else {
|
||||||
|
print!("{:02X} ", self.inner[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
|
||||||
|
// --- ASCII LINE ---
|
||||||
|
print!("Ascii: ");
|
||||||
|
for i in start..end {
|
||||||
|
let b = self.inner[i];
|
||||||
|
let c = if b.is_ascii_graphic() || b == b' ' {
|
||||||
|
b as char
|
||||||
|
} else {
|
||||||
|
'.'
|
||||||
|
};
|
||||||
|
|
||||||
|
if i == self.pos {
|
||||||
|
print!("[{}] ", c);
|
||||||
|
} else {
|
||||||
|
print!("{} ", c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
|
||||||
|
// --- OFFSET LINE ---
|
||||||
|
print!("Offset: ");
|
||||||
|
for i in start..end {
|
||||||
|
let off = i as isize - self.pos as isize;
|
||||||
|
if i == self.pos {
|
||||||
|
print!("[{}] ", off);
|
||||||
|
} else {
|
||||||
|
print!("{:<3} ", off);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remaining(&mut self) -> &'a [u8] {
|
||||||
|
let res = &self.inner[self.pos..];
|
||||||
|
self.pos = self.inner.len();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_u8(&mut self) -> Option<u8> {
|
||||||
|
if self.pos == self.inner.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let res = Some(self.inner[self.pos]);
|
||||||
|
self.pos += 1;
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_le_u16(&mut self) -> Option<u16> {
|
||||||
|
const SIZE: usize = 2;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(u16::from_le_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_be_u16(&mut self) -> Option<u16> {
|
||||||
|
const SIZE: usize = 2;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(u16::from_be_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_le_u32(&mut self) -> Option<u32> {
|
||||||
|
const SIZE: usize = 4;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(u32::from_le_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_be_u32(&mut self) -> Option<u32> {
|
||||||
|
const SIZE: usize = 4;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(u32::from_be_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_le_u64(&mut self) -> Option<u64> {
|
||||||
|
const SIZE: usize = 8;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(u64::from_le_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_be_u64(&mut self) -> Option<u64> {
|
||||||
|
const SIZE: usize = 8;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(u64::from_be_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_le_u128(&mut self) -> Option<u128> {
|
||||||
|
const SIZE: usize = 16;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(u128::from_le_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_be_u128(&mut self) -> Option<u128> {
|
||||||
|
const SIZE: usize = 16;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(u128::from_be_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_le_f32(&mut self) -> Option<f32> {
|
||||||
|
const SIZE: usize = 4;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(f32::from_le_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_be_f32(&mut self) -> Option<f32> {
|
||||||
|
const SIZE: usize = 4;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(f32::from_be_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_i8(&mut self) -> Option<i8> {
|
||||||
|
if self.pos == self.inner.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let res = Some(self.inner[self.pos]).map(|x| x as i8);
|
||||||
|
self.pos += 1;
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_le_i16(&mut self) -> Option<i16> {
|
||||||
|
const SIZE: usize = 2;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(i16::from_le_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_be_i16(&mut self) -> Option<i16> {
|
||||||
|
const SIZE: usize = 2;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(i16::from_be_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_le_i32(&mut self) -> Option<i32> {
|
||||||
|
const SIZE: usize = 4;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(i32::from_le_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_be_i32(&mut self) -> Option<i32> {
|
||||||
|
const SIZE: usize = 4;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(i32::from_be_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_le_i64(&mut self) -> Option<i64> {
|
||||||
|
const SIZE: usize = 8;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(i64::from_le_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_be_i64(&mut self) -> Option<i64> {
|
||||||
|
const SIZE: usize = 8;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(i64::from_be_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_le_i128(&mut self) -> Option<i128> {
|
||||||
|
const SIZE: usize = 16;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(i128::from_le_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_be_i128(&mut self) -> Option<i128> {
|
||||||
|
const SIZE: usize = 16;
|
||||||
|
let bytes = self.read(SIZE)?;
|
||||||
|
let bytes: [u8; SIZE] = bytes.try_into().unwrap();
|
||||||
|
Some(i128::from_be_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_2(&mut self) -> Option<[u8; 2]> {
|
||||||
|
let bytes = self.read(2)?;
|
||||||
|
Some(bytes.to_owned().try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_3(&mut self) -> Option<[u8; 3]> {
|
||||||
|
let bytes = self.read(3)?;
|
||||||
|
Some(bytes.to_owned().try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_4(&mut self) -> Option<[u8; 4]> {
|
||||||
|
let bytes = self.read(4)?;
|
||||||
|
Some(bytes.to_owned().try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_8(&mut self) -> Option<[u8; 8]> {
|
||||||
|
let bytes = self.read(8)?;
|
||||||
|
Some(bytes.to_owned().try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_20(&mut self) -> Option<[u8; 20]> {
|
||||||
|
let bytes = self.read(20)?;
|
||||||
|
Some(bytes.to_owned().try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_32(&mut self) -> Option<[u8; 32]> {
|
||||||
|
let bytes = self.read(32)?;
|
||||||
|
Some(bytes.to_owned().try_into().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![warn(missing_debug_implementations)]
|
||||||
|
#![warn(missing_copy_implementations)]
|
||||||
// Jackson Coxson
|
// Jackson Coxson
|
||||||
|
|
||||||
#[cfg(feature = "pair")]
|
#[cfg(all(feature = "pair", feature = "rustls"))]
|
||||||
mod ca;
|
mod ca;
|
||||||
|
pub mod cursor;
|
||||||
|
mod obfuscation;
|
||||||
pub mod pairing_file;
|
pub mod pairing_file;
|
||||||
mod plist_macro;
|
|
||||||
pub mod provider;
|
pub mod provider;
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
mod sni;
|
mod sni;
|
||||||
#[cfg(feature = "tunnel_tcp_stack")]
|
#[cfg(feature = "tunnel_tcp_stack")]
|
||||||
pub mod tcp;
|
pub mod tcp;
|
||||||
@@ -15,7 +19,6 @@ pub mod tss;
|
|||||||
pub mod tunneld;
|
pub mod tunneld;
|
||||||
#[cfg(feature = "usbmuxd")]
|
#[cfg(feature = "usbmuxd")]
|
||||||
pub mod usbmuxd;
|
pub mod usbmuxd;
|
||||||
mod util;
|
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
#[cfg(feature = "xpc")]
|
#[cfg(feature = "xpc")]
|
||||||
pub mod xpc;
|
pub mod xpc;
|
||||||
@@ -26,8 +29,9 @@ pub use services::*;
|
|||||||
#[cfg(feature = "xpc")]
|
#[cfg(feature = "xpc")]
|
||||||
pub use xpc::RemoteXpcClient;
|
pub use xpc::RemoteXpcClient;
|
||||||
|
|
||||||
use log::{debug, error, trace};
|
use plist_macro::{plist, pretty_print_dictionary, pretty_print_plist};
|
||||||
use provider::{IdeviceProvider, RsdProvider};
|
use provider::{IdeviceProvider, RsdProvider};
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
use rustls::{crypto::CryptoProvider, pki_types::ServerName};
|
use rustls::{crypto::CryptoProvider, pki_types::ServerName};
|
||||||
use std::{
|
use std::{
|
||||||
io::{self, BufWriter},
|
io::{self, BufWriter},
|
||||||
@@ -35,8 +39,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||||
|
use tracing::{debug, trace};
|
||||||
pub use util::{pretty_print_dictionary, pretty_print_plist};
|
|
||||||
|
|
||||||
use crate::services::lockdown::LockdownClient;
|
use crate::services::lockdown::LockdownClient;
|
||||||
|
|
||||||
@@ -72,6 +75,22 @@ pub trait IdeviceService: Sized {
|
|||||||
#[allow(async_fn_in_trait)]
|
#[allow(async_fn_in_trait)]
|
||||||
async fn connect(provider: &dyn IdeviceProvider) -> Result<Self, IdeviceError> {
|
async fn connect(provider: &dyn IdeviceProvider) -> Result<Self, IdeviceError> {
|
||||||
let mut lockdown = LockdownClient::connect(provider).await?;
|
let mut lockdown = LockdownClient::connect(provider).await?;
|
||||||
|
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
let legacy = lockdown
|
||||||
|
.get_value(Some("ProductVersion"), None)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|x| x.as_string())
|
||||||
|
.and_then(|x| x.split(".").next())
|
||||||
|
.and_then(|x| x.parse::<u8>().ok())
|
||||||
|
.map(|x| x < 5)
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "openssl"))]
|
||||||
|
let legacy = false;
|
||||||
|
|
||||||
lockdown
|
lockdown
|
||||||
.start_session(&provider.get_pairing_file().await?)
|
.start_session(&provider.get_pairing_file().await?)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -86,7 +105,7 @@ pub trait IdeviceService: Sized {
|
|||||||
let mut idevice = provider.connect(port).await?;
|
let mut idevice = provider.connect(port).await?;
|
||||||
if ssl {
|
if ssl {
|
||||||
idevice
|
idevice
|
||||||
.start_session(&provider.get_pairing_file().await?)
|
.start_session(&provider.get_pairing_file().await?, legacy)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,6 +147,7 @@ pub type IdeviceSocket = Box<dyn ReadWrite>;
|
|||||||
///
|
///
|
||||||
/// Manages the connection socket and provides methods for common device operations
|
/// Manages the connection socket and provides methods for common device operations
|
||||||
/// and message exchange.
|
/// and message exchange.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Idevice {
|
pub struct Idevice {
|
||||||
/// The underlying connection socket, boxed for dynamic dispatch
|
/// The underlying connection socket, boxed for dynamic dispatch
|
||||||
socket: Option<Box<dyn ReadWrite>>,
|
socket: Option<Box<dyn ReadWrite>>,
|
||||||
@@ -175,7 +195,7 @@ impl Idevice {
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns `IdeviceError` if communication fails or response is invalid
|
/// Returns `IdeviceError` if communication fails or response is invalid
|
||||||
pub async fn get_type(&mut self) -> Result<String, IdeviceError> {
|
pub async fn get_type(&mut self) -> Result<String, IdeviceError> {
|
||||||
let req = crate::plist!({
|
let req = plist!({
|
||||||
"Label": self.label.clone(),
|
"Label": self.label.clone(),
|
||||||
"Request": "QueryType",
|
"Request": "QueryType",
|
||||||
});
|
});
|
||||||
@@ -195,7 +215,7 @@ impl Idevice {
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns `IdeviceError` if the protocol sequence isn't followed correctly
|
/// Returns `IdeviceError` if the protocol sequence isn't followed correctly
|
||||||
pub async fn rsd_checkin(&mut self) -> Result<(), IdeviceError> {
|
pub async fn rsd_checkin(&mut self) -> Result<(), IdeviceError> {
|
||||||
let req = crate::plist!({
|
let req = plist!({
|
||||||
"Label": self.label.clone(),
|
"Label": self.label.clone(),
|
||||||
"ProtocolVersion": "2",
|
"ProtocolVersion": "2",
|
||||||
"Request": "RSDCheckin",
|
"Request": "RSDCheckin",
|
||||||
@@ -287,6 +307,75 @@ impl Idevice {
|
|||||||
self.send_raw_with_progress(message, |_| async {}, ()).await
|
self.send_raw_with_progress(message, |_| async {}, ()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends raw binary data via vectored I/O
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `bufs` - The buffers to send
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns `IdeviceError` if transmission fails
|
||||||
|
pub async fn send_raw_vectored(
|
||||||
|
&mut self,
|
||||||
|
bufs: &[std::io::IoSlice<'_>],
|
||||||
|
) -> Result<(), IdeviceError> {
|
||||||
|
if let Some(socket) = &mut self.socket {
|
||||||
|
let mut curr_idx = 0;
|
||||||
|
let mut curr_offset = 0;
|
||||||
|
|
||||||
|
while curr_idx < bufs.len() {
|
||||||
|
let mut iovec = Vec::new();
|
||||||
|
let mut accumulated_len = 0;
|
||||||
|
let max_chunk = 1024 * 64;
|
||||||
|
|
||||||
|
// Add partial first slice
|
||||||
|
let first_avail = bufs[curr_idx].len() - curr_offset;
|
||||||
|
let to_take_first = std::cmp::min(first_avail, max_chunk);
|
||||||
|
iovec.push(std::io::IoSlice::new(
|
||||||
|
&bufs[curr_idx][curr_offset..curr_offset + to_take_first],
|
||||||
|
));
|
||||||
|
accumulated_len += to_take_first;
|
||||||
|
|
||||||
|
// Add others up to max_chunk
|
||||||
|
let mut temp_idx = curr_idx + 1;
|
||||||
|
while temp_idx < bufs.len() && accumulated_len < max_chunk {
|
||||||
|
let needed = max_chunk - accumulated_len;
|
||||||
|
let avail = bufs[temp_idx].len();
|
||||||
|
let take = std::cmp::min(avail, needed);
|
||||||
|
iovec.push(std::io::IoSlice::new(&bufs[temp_idx][..take]));
|
||||||
|
accumulated_len += take;
|
||||||
|
temp_idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let n = socket.write_vectored(&iovec).await?;
|
||||||
|
if n == 0 {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::WriteZero,
|
||||||
|
"failed to write whole buffer",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance cursor by n
|
||||||
|
let mut advanced = n;
|
||||||
|
while advanced > 0 && curr_idx < bufs.len() {
|
||||||
|
let available = bufs[curr_idx].len() - curr_offset;
|
||||||
|
if advanced < available {
|
||||||
|
curr_offset += advanced;
|
||||||
|
advanced = 0;
|
||||||
|
} else {
|
||||||
|
advanced -= available;
|
||||||
|
curr_idx += 1;
|
||||||
|
curr_offset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
socket.flush().await?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(IdeviceError::NoEstablishedConnection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Sends raw binary data with progress callbacks
|
/// Sends raw binary data with progress callbacks
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -389,14 +478,20 @@ impl Idevice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Error is not a string or integer from read_plist: {e:?}");
|
tracing::error!("Error is not a string or integer from read_plist: {e:?}");
|
||||||
return Err(IdeviceError::UnexpectedResponse);
|
return Err(IdeviceError::UnexpectedResponse);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some(e) = IdeviceError::from_device_error_type(e.as_str(), &res) {
|
if let Some(e) = IdeviceError::from_device_error_type(e.as_str(), &res) {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
} else {
|
} else {
|
||||||
return Err(IdeviceError::UnknownErrorType(e));
|
let msg =
|
||||||
|
if let Some(desc) = res.get("ErrorDescription").and_then(|x| x.as_string()) {
|
||||||
|
format!("{} ({})", e, desc)
|
||||||
|
} else {
|
||||||
|
e
|
||||||
|
};
|
||||||
|
return Err(IdeviceError::UnknownErrorType(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(res)
|
Ok(res)
|
||||||
@@ -459,7 +554,16 @@ impl Idevice {
|
|||||||
pub async fn start_session(
|
pub async fn start_session(
|
||||||
&mut self,
|
&mut self,
|
||||||
pairing_file: &pairing_file::PairingFile,
|
pairing_file: &pairing_file::PairingFile,
|
||||||
|
legacy: bool,
|
||||||
) -> Result<(), IdeviceError> {
|
) -> Result<(), IdeviceError> {
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
{
|
||||||
|
if legacy {
|
||||||
|
tracing::warn!(
|
||||||
|
"Compiled with rustls, but connecting to legacy device! rustls does not support old SSL, this will fail."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if CryptoProvider::get_default().is_none() {
|
if CryptoProvider::get_default().is_none() {
|
||||||
// rust-analyzer will choke on this block, don't worry about it
|
// rust-analyzer will choke on this block, don't worry about it
|
||||||
let crypto_provider: CryptoProvider = {
|
let crypto_provider: CryptoProvider = {
|
||||||
@@ -488,7 +592,9 @@ impl Idevice {
|
|||||||
// My sanity while debugging the workspace crates are more important.
|
// My sanity while debugging the workspace crates are more important.
|
||||||
|
|
||||||
debug!("Using ring crypto backend, because both were passed");
|
debug!("Using ring crypto backend, because both were passed");
|
||||||
log::warn!("Both ring && aws-lc are selected as idevice crypto backends!");
|
tracing::warn!(
|
||||||
|
"Both ring && aws-lc are selected as idevice crypto backends!"
|
||||||
|
);
|
||||||
rustls::crypto::ring::default_provider()
|
rustls::crypto::ring::default_provider()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -497,7 +603,7 @@ impl Idevice {
|
|||||||
// For whatever reason, getting the default provider will return None on iOS at
|
// For whatever reason, getting the default provider will return None on iOS at
|
||||||
// random. Installing the default provider a second time will return an error, so
|
// random. Installing the default provider a second time will return an error, so
|
||||||
// we will log it but not propogate it. An issue should be opened with rustls.
|
// we will log it but not propogate it. An issue should be opened with rustls.
|
||||||
log::error!("Failed to set crypto provider: {e:?}");
|
tracing::error!("Failed to set crypto provider: {e:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let config = sni::create_client_config(pairing_file)?;
|
let config = sni::create_client_config(pairing_file)?;
|
||||||
@@ -512,6 +618,30 @@ impl Idevice {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
|
||||||
|
{
|
||||||
|
let mut connector =
|
||||||
|
openssl::ssl::SslConnector::builder(openssl::ssl::SslMethod::tls())?;
|
||||||
|
if legacy {
|
||||||
|
connector.set_min_proto_version(Some(openssl::ssl::SslVersion::SSL3))?;
|
||||||
|
connector.set_max_proto_version(Some(openssl::ssl::SslVersion::TLS1))?;
|
||||||
|
connector.set_cipher_list("ALL:!aNULL:!eNULL:@SECLEVEL=0")?;
|
||||||
|
connector.set_options(openssl::ssl::SslOptions::ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut connector = connector.build().configure()?.into_ssl("ur mom")?;
|
||||||
|
|
||||||
|
connector.set_certificate(&pairing_file.host_certificate)?;
|
||||||
|
connector.set_private_key(&pairing_file.host_private_key)?;
|
||||||
|
connector.set_verify(openssl::ssl::SslVerifyMode::empty());
|
||||||
|
let socket = self.socket.take().unwrap();
|
||||||
|
let mut ssl_stream = tokio_openssl::SslStream::new(connector, socket)?;
|
||||||
|
std::pin::Pin::new(&mut ssl_stream).connect().await?;
|
||||||
|
self.socket = Some(Box::new(ssl_stream));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Comprehensive error type for all device communication failures
|
/// Comprehensive error type for all device communication failures
|
||||||
@@ -521,12 +651,24 @@ impl Idevice {
|
|||||||
pub enum IdeviceError {
|
pub enum IdeviceError {
|
||||||
#[error("device socket io failed")]
|
#[error("device socket io failed")]
|
||||||
Socket(#[from] io::Error) = -1,
|
Socket(#[from] io::Error) = -1,
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
#[error("PEM parse failed")]
|
#[error("PEM parse failed")]
|
||||||
PemParseFailed(#[from] rustls::pki_types::pem::Error) = -2,
|
PemParseFailed(#[from] rustls::pki_types::pem::Error) = -2,
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
#[error("TLS error")]
|
#[error("TLS error")]
|
||||||
Rustls(#[from] rustls::Error) = -3,
|
Rustls(#[from] rustls::Error) = -3,
|
||||||
|
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
|
||||||
|
#[error("TLS error")]
|
||||||
|
Rustls(#[from] openssl::ssl::Error) = -3,
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
#[error("TLS verifiction build failed")]
|
#[error("TLS verifiction build failed")]
|
||||||
TlsBuilderFailed(#[from] rustls::server::VerifierBuilderError) = -4,
|
TlsBuilderFailed(#[from] rustls::server::VerifierBuilderError) = -4,
|
||||||
|
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
|
||||||
|
#[error("TLS verifiction build failed")]
|
||||||
|
TlsBuilderFailed(#[from] openssl::error::ErrorStack) = -4,
|
||||||
|
|
||||||
#[error("io on plist")]
|
#[error("io on plist")]
|
||||||
Plist(#[from] plist::Error) = -5,
|
Plist(#[from] plist::Error) = -5,
|
||||||
#[error("can't convert bytes to utf8")]
|
#[error("can't convert bytes to utf8")]
|
||||||
@@ -723,25 +865,6 @@ pub enum IdeviceError {
|
|||||||
|
|
||||||
#[error("Developer mode is not enabled")]
|
#[error("Developer mode is not enabled")]
|
||||||
DeveloperModeNotEnabled = -68,
|
DeveloperModeNotEnabled = -68,
|
||||||
|
|
||||||
#[cfg(feature = "remote_pairing")]
|
|
||||||
#[error("could not parse as JSON")]
|
|
||||||
JsonParseFailed(#[from] json::Error) = -69,
|
|
||||||
|
|
||||||
#[cfg(feature = "remote_pairing")]
|
|
||||||
#[error("unknown TLV type: {0}")]
|
|
||||||
UnknownTlv(u8) = -70,
|
|
||||||
|
|
||||||
#[cfg(feature = "remote_pairing")]
|
|
||||||
#[error("malformed TLV")]
|
|
||||||
MalformedTlv = -71,
|
|
||||||
|
|
||||||
#[error("failed to decode base64 string")]
|
|
||||||
Base64Decode(#[from] base64::DecodeError) = -72,
|
|
||||||
|
|
||||||
#[cfg(feature = "remote_pairing")]
|
|
||||||
#[error("pair verify failed")]
|
|
||||||
PairVerifyFailed = -73,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdeviceError {
|
impl IdeviceError {
|
||||||
@@ -792,6 +915,7 @@ impl IdeviceError {
|
|||||||
pub fn code(&self) -> i32 {
|
pub fn code(&self) -> i32 {
|
||||||
match self {
|
match self {
|
||||||
IdeviceError::Socket(_) => -1,
|
IdeviceError::Socket(_) => -1,
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
IdeviceError::PemParseFailed(_) => -2,
|
IdeviceError::PemParseFailed(_) => -2,
|
||||||
IdeviceError::Rustls(_) => -3,
|
IdeviceError::Rustls(_) => -3,
|
||||||
IdeviceError::TlsBuilderFailed(_) => -4,
|
IdeviceError::TlsBuilderFailed(_) => -4,
|
||||||
@@ -906,15 +1030,6 @@ impl IdeviceError {
|
|||||||
#[cfg(feature = "installation_proxy")]
|
#[cfg(feature = "installation_proxy")]
|
||||||
IdeviceError::MalformedPackageArchive(_) => -67,
|
IdeviceError::MalformedPackageArchive(_) => -67,
|
||||||
IdeviceError::DeveloperModeNotEnabled => -68,
|
IdeviceError::DeveloperModeNotEnabled => -68,
|
||||||
#[cfg(feature = "remote_pairing")]
|
|
||||||
IdeviceError::JsonParseFailed(_) => -69,
|
|
||||||
#[cfg(feature = "remote_pairing")]
|
|
||||||
IdeviceError::UnknownTlv(_) => -70,
|
|
||||||
#[cfg(feature = "remote_pairing")]
|
|
||||||
IdeviceError::MalformedTlv => -71,
|
|
||||||
IdeviceError::Base64Decode(_) => -72,
|
|
||||||
#[cfg(feature = "remote_pairing")]
|
|
||||||
IdeviceError::PairVerifyFailed => -73,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
idevice/src/obfuscation.rs
Normal file
15
idevice/src/obfuscation.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! obf {
|
||||||
|
($lit:literal) => {{
|
||||||
|
#[cfg(feature = "obfuscate")]
|
||||||
|
{
|
||||||
|
std::borrow::Cow::Owned(obfstr::obfstr!($lit).to_string())
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "obfuscate"))]
|
||||||
|
{
|
||||||
|
std::borrow::Cow::Borrowed($lit)
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
@@ -5,15 +5,22 @@
|
|||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use log::warn;
|
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
|
||||||
|
use openssl::{
|
||||||
|
pkey::{PKey, Private},
|
||||||
|
x509::X509,
|
||||||
|
};
|
||||||
use plist::Data;
|
use plist::Data;
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
use rustls::pki_types::{CertificateDer, pem::PemObject};
|
use rustls::pki_types::{CertificateDer, pem::PemObject};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
/// Represents a complete iOS device pairing record
|
/// Represents a complete iOS device pairing record
|
||||||
///
|
///
|
||||||
/// Contains all cryptographic materials and identifiers needed for secure communication
|
/// Contains all cryptographic materials and identifiers needed for secure communication
|
||||||
/// with an iOS device, including certificates, private keys, and device identifiers.
|
/// with an iOS device, including certificates, private keys, and device identifiers.
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PairingFile {
|
pub struct PairingFile {
|
||||||
/// Device's certificate in DER format
|
/// Device's certificate in DER format
|
||||||
@@ -31,13 +38,28 @@ pub struct PairingFile {
|
|||||||
/// Host identifier
|
/// Host identifier
|
||||||
pub host_id: String,
|
pub host_id: String,
|
||||||
/// Escrow bag allowing for access while locked
|
/// Escrow bag allowing for access while locked
|
||||||
pub escrow_bag: Vec<u8>,
|
pub escrow_bag: Option<Vec<u8>>,
|
||||||
/// Device's WiFi MAC address
|
/// Device's WiFi MAC address
|
||||||
pub wifi_mac_address: String,
|
pub wifi_mac_address: String,
|
||||||
/// Device's Unique Device Identifier (optional)
|
/// Device's Unique Device Identifier (optional)
|
||||||
pub udid: Option<String>,
|
pub udid: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PairingFile {
|
||||||
|
pub device_certificate: X509,
|
||||||
|
pub host_private_key: PKey<Private>,
|
||||||
|
pub host_certificate: X509,
|
||||||
|
pub root_private_key: PKey<Private>,
|
||||||
|
pub root_certificate: X509,
|
||||||
|
pub system_buid: String,
|
||||||
|
pub host_id: String,
|
||||||
|
pub escrow_bag: Vec<u8>,
|
||||||
|
pub wifi_mac_address: String,
|
||||||
|
pub udid: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Internal representation of a pairing file for serialization/deserialization
|
/// Internal representation of a pairing file for serialization/deserialization
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
#[serde(rename_all = "PascalCase")]
|
#[serde(rename_all = "PascalCase")]
|
||||||
@@ -51,7 +73,7 @@ struct RawPairingFile {
|
|||||||
system_buid: String,
|
system_buid: String,
|
||||||
#[serde(rename = "HostID")]
|
#[serde(rename = "HostID")]
|
||||||
host_id: String,
|
host_id: String,
|
||||||
escrow_bag: Data,
|
escrow_bag: Option<Data>, // None on Apple Watch
|
||||||
#[serde(rename = "WiFiMACAddress")]
|
#[serde(rename = "WiFiMACAddress")]
|
||||||
wifi_mac_address: String,
|
wifi_mac_address: String,
|
||||||
#[serde(rename = "UDID")]
|
#[serde(rename = "UDID")]
|
||||||
@@ -133,6 +155,7 @@ impl PairingFile {
|
|||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns `IdeviceError` if serialization fails
|
/// Returns `IdeviceError` if serialization fails
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
pub fn serialize(self) -> Result<Vec<u8>, crate::IdeviceError> {
|
pub fn serialize(self) -> Result<Vec<u8>, crate::IdeviceError> {
|
||||||
let raw = RawPairingFile::from(self);
|
let raw = RawPairingFile::from(self);
|
||||||
|
|
||||||
@@ -140,8 +163,18 @@ impl PairingFile {
|
|||||||
plist::to_writer_xml(&mut buf, &raw)?;
|
plist::to_writer_xml(&mut buf, &raw)?;
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
|
||||||
|
pub fn serialize(self) -> Result<Vec<u8>, crate::IdeviceError> {
|
||||||
|
let raw = RawPairingFile::try_from(self)?;
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
plist::to_writer_xml(&mut buf, &raw)?;
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
impl TryFrom<RawPairingFile> for PairingFile {
|
impl TryFrom<RawPairingFile> for PairingFile {
|
||||||
type Error = rustls::pki_types::pem::Error;
|
type Error = rustls::pki_types::pem::Error;
|
||||||
|
|
||||||
@@ -173,13 +206,38 @@ impl TryFrom<RawPairingFile> for PairingFile {
|
|||||||
root_certificate: CertificateDer::from_pem_slice(&root_certificate_pem)?,
|
root_certificate: CertificateDer::from_pem_slice(&root_certificate_pem)?,
|
||||||
system_buid: value.system_buid,
|
system_buid: value.system_buid,
|
||||||
host_id: value.host_id,
|
host_id: value.host_id,
|
||||||
escrow_bag: value.escrow_bag.into(),
|
escrow_bag: value.escrow_bag.map(|x| x.into()),
|
||||||
wifi_mac_address: value.wifi_mac_address,
|
wifi_mac_address: value.wifi_mac_address,
|
||||||
udid: value.udid,
|
udid: value.udid,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
|
||||||
|
impl TryFrom<RawPairingFile> for PairingFile {
|
||||||
|
type Error = openssl::error::ErrorStack;
|
||||||
|
|
||||||
|
fn try_from(value: RawPairingFile) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
device_certificate: X509::from_pem(&Into::<Vec<u8>>::into(value.device_certificate))?,
|
||||||
|
host_private_key: PKey::private_key_from_pem(&Into::<Vec<u8>>::into(
|
||||||
|
value.host_private_key,
|
||||||
|
))?,
|
||||||
|
host_certificate: X509::from_pem(&Into::<Vec<u8>>::into(value.host_certificate))?,
|
||||||
|
root_private_key: PKey::private_key_from_pem(&Into::<Vec<u8>>::into(
|
||||||
|
value.root_private_key,
|
||||||
|
))?,
|
||||||
|
root_certificate: X509::from_pem(&Into::<Vec<u8>>::into(value.root_certificate))?,
|
||||||
|
system_buid: value.system_buid,
|
||||||
|
host_id: value.host_id,
|
||||||
|
escrow_bag: value.escrow_bag.map(|x| x.into()),
|
||||||
|
wifi_mac_address: value.wifi_mac_address,
|
||||||
|
udid: value.udid,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
impl From<PairingFile> for RawPairingFile {
|
impl From<PairingFile> for RawPairingFile {
|
||||||
/// Converts a structured pairing file into a raw pairing file for serialization
|
/// Converts a structured pairing file into a raw pairing file for serialization
|
||||||
fn from(value: PairingFile) -> Self {
|
fn from(value: PairingFile) -> Self {
|
||||||
@@ -200,13 +258,33 @@ impl From<PairingFile> for RawPairingFile {
|
|||||||
root_certificate: Data::new(root_cert_data),
|
root_certificate: Data::new(root_cert_data),
|
||||||
system_buid: value.system_buid,
|
system_buid: value.system_buid,
|
||||||
host_id: value.host_id.clone(),
|
host_id: value.host_id.clone(),
|
||||||
escrow_bag: Data::new(value.escrow_bag),
|
escrow_bag: value.escrow_bag.map(Data::new),
|
||||||
wifi_mac_address: value.wifi_mac_address,
|
wifi_mac_address: value.wifi_mac_address,
|
||||||
udid: value.udid,
|
udid: value.udid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
|
||||||
|
impl TryFrom<PairingFile> for RawPairingFile {
|
||||||
|
type Error = openssl::error::ErrorStack;
|
||||||
|
|
||||||
|
fn try_from(value: PairingFile) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
device_certificate: Data::new(value.device_certificate.to_pem()?),
|
||||||
|
host_private_key: Data::new(value.host_private_key.private_key_to_pem_pkcs8()?),
|
||||||
|
host_certificate: Data::new(value.host_certificate.to_pem()?),
|
||||||
|
root_private_key: Data::new(value.root_private_key.private_key_to_pem_pkcs8()?),
|
||||||
|
root_certificate: Data::new(value.root_certificate.to_pem()?),
|
||||||
|
system_buid: value.system_buid,
|
||||||
|
host_id: value.host_id.clone(),
|
||||||
|
escrow_bag: value.escrow_bag.map(Data::new),
|
||||||
|
wifi_mac_address: value.wifi_mac_address,
|
||||||
|
udid: value.udid,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper function to ensure data has proper PEM headers
|
/// Helper function to ensure data has proper PEM headers
|
||||||
/// If the data already has headers, it returns it as is
|
/// If the data already has headers, it returns it as is
|
||||||
/// If not, it adds the appropriate BEGIN and END headers
|
/// If not, it adds the appropriate BEGIN and END headers
|
||||||
|
|||||||
@@ -1,714 +0,0 @@
|
|||||||
// Jackson Coxson
|
|
||||||
// Ported from serde's json!
|
|
||||||
|
|
||||||
/// Construct a `plist::Value` from a JSON-like literal.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use idevice::plist;
|
|
||||||
/// #
|
|
||||||
/// let value = plist!({
|
|
||||||
/// "code": 200,
|
|
||||||
/// "success": true,
|
|
||||||
/// "payload": {
|
|
||||||
/// "features": [
|
|
||||||
/// "serde",
|
|
||||||
/// "plist"
|
|
||||||
/// ],
|
|
||||||
/// "homepage": null
|
|
||||||
/// }
|
|
||||||
/// });
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Variables or expressions can be interpolated into the plist literal. Any type
|
|
||||||
/// interpolated into an array element or object value must implement `Into<plist::Value>`.
|
|
||||||
/// If the conversion fails, the `plist!` macro will panic.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use idevice::plist;
|
|
||||||
/// #
|
|
||||||
/// let code = 200;
|
|
||||||
/// let features = vec!["serde", "plist"];
|
|
||||||
///
|
|
||||||
/// let value = plist!({
|
|
||||||
/// "code": code,
|
|
||||||
/// "success": code == 200,
|
|
||||||
/// "payload": {
|
|
||||||
/// features[0]: features[1]
|
|
||||||
/// }
|
|
||||||
/// });
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Trailing commas are allowed inside both arrays and objects.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use idevice::plist;
|
|
||||||
/// #
|
|
||||||
/// let value = plist!([
|
|
||||||
/// "notice",
|
|
||||||
/// "the",
|
|
||||||
/// "trailing",
|
|
||||||
/// "comma -->",
|
|
||||||
/// ]);
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! plist {
|
|
||||||
// Force: dictionary out
|
|
||||||
(dict { $($tt:tt)+ }) => {{
|
|
||||||
let mut object = plist::Dictionary::new();
|
|
||||||
$crate::plist_internal!(@object object () ($($tt)+) ($($tt)+));
|
|
||||||
object
|
|
||||||
}};
|
|
||||||
|
|
||||||
// Force: value out (explicit, though default already does this)
|
|
||||||
(value { $($tt:tt)+ }) => {
|
|
||||||
$crate::plist_internal!({ $($tt)+ })
|
|
||||||
};
|
|
||||||
|
|
||||||
// Force: raw vec of plist::Value out
|
|
||||||
(array [ $($tt:tt)+ ]) => {
|
|
||||||
$crate::plist_internal!(@array [] $($tt)+)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hide distracting implementation details from the generated rustdoc.
|
|
||||||
($($plist:tt)+) => {
|
|
||||||
$crate::plist_internal!($($plist)+)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
#[doc(hidden)]
|
|
||||||
macro_rules! plist_internal {
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// TT muncher for parsing the inside of an array [...]. Produces a vec![...]
|
|
||||||
// of the elements.
|
|
||||||
//
|
|
||||||
// Must be invoked as: plist_internal!(@array [] $($tt)*)
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Done with trailing comma.
|
|
||||||
(@array [$($elems:expr,)*]) => {
|
|
||||||
vec![$($elems,)*]
|
|
||||||
};
|
|
||||||
|
|
||||||
// Done without trailing comma.
|
|
||||||
(@array [$($elems:expr),*]) => {
|
|
||||||
vec![$($elems),*]
|
|
||||||
};
|
|
||||||
|
|
||||||
// Next element is `null`.
|
|
||||||
(@array [$($elems:expr,)*] null $($rest:tt)*) => {
|
|
||||||
$crate::plist_internal!(@array [$($elems,)* $crate::plist_internal!(null)] $($rest)*)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Next element is `true`.
|
|
||||||
(@array [$($elems:expr,)*] true $($rest:tt)*) => {
|
|
||||||
$crate::plist_internal!(@array [$($elems,)* $crate::plist_internal!(true)] $($rest)*)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Next element is `false`.
|
|
||||||
(@array [$($elems:expr,)*] false $($rest:tt)*) => {
|
|
||||||
$crate::plist_internal!(@array [$($elems,)* $crate::plist_internal!(false)] $($rest)*)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Next element is an array.
|
|
||||||
(@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
|
|
||||||
$crate::plist_internal!(@array [$($elems,)* $crate::plist_internal!([$($array)*])] $($rest)*)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Next element is a map.
|
|
||||||
(@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
|
|
||||||
$crate::plist_internal!(@array [$($elems,)* $crate::plist_internal!({$($map)*})] $($rest)*)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Next element is an expression followed by comma.
|
|
||||||
(@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
|
|
||||||
$crate::plist_internal!(@array [$($elems,)* $crate::plist_internal!($next),] $($rest)*)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Last element is an expression with no trailing comma.
|
|
||||||
(@array [$($elems:expr,)*] $last:expr) => {
|
|
||||||
$crate::plist_internal!(@array [$($elems,)* $crate::plist_internal!($last)])
|
|
||||||
};
|
|
||||||
|
|
||||||
// Comma after the most recent element.
|
|
||||||
(@array [$($elems:expr),*] , $($rest:tt)*) => {
|
|
||||||
$crate::plist_internal!(@array [$($elems,)*] $($rest)*)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Unexpected token after most recent element.
|
|
||||||
(@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
|
|
||||||
$crate::plist_unexpected!($unexpected)
|
|
||||||
};
|
|
||||||
|
|
||||||
(@array [$($elems:expr,)*] ? $maybe:expr , $($rest:tt)*) => {
|
|
||||||
if let Some(__v) = $crate::plist_macro::plist_maybe($maybe) {
|
|
||||||
$crate::plist_internal!(@array [$($elems,)* __v,] $($rest)*)
|
|
||||||
} else {
|
|
||||||
$crate::plist_internal!(@array [$($elems,)*] $($rest)*)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(@array [$($elems:expr,)*] ? $maybe:expr) => {
|
|
||||||
if let Some(__v) = $crate::plist_macro::plist_maybe($maybe) {
|
|
||||||
$crate::plist_internal!(@array [$($elems,)* __v])
|
|
||||||
} else {
|
|
||||||
$crate::plist_internal!(@array [$($elems,)*])
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// TT muncher for parsing the inside of an object {...}. Each entry is
|
|
||||||
// inserted into the given map variable.
|
|
||||||
//
|
|
||||||
// Must be invoked as: plist_internal!(@object $map () ($($tt)*) ($($tt)*))
|
|
||||||
//
|
|
||||||
// We require two copies of the input tokens so that we can match on one
|
|
||||||
// copy and trigger errors on the other copy.
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Done.
|
|
||||||
(@object $object:ident () () ()) => {};
|
|
||||||
|
|
||||||
// Insert the current entry followed by trailing comma.
|
|
||||||
(@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
|
|
||||||
let _ = $object.insert(($($key)+).into(), $value);
|
|
||||||
$crate::plist_internal!(@object $object () ($($rest)*) ($($rest)*));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Current entry followed by unexpected token.
|
|
||||||
(@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
|
|
||||||
$crate::plist_unexpected!($unexpected);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Insert the last entry without trailing comma.
|
|
||||||
(@object $object:ident [$($key:tt)+] ($value:expr)) => {
|
|
||||||
let _ = $object.insert(($($key)+).into(), $value);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Next value is `null`.
|
|
||||||
(@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
|
|
||||||
$crate::plist_internal!(@object $object [$($key)+] ($crate::plist_internal!(null)) $($rest)*);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Next value is `true`.
|
|
||||||
(@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => {
|
|
||||||
$crate::plist_internal!(@object $object [$($key)+] ($crate::plist_internal!(true)) $($rest)*);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Next value is `false`.
|
|
||||||
(@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => {
|
|
||||||
$crate::plist_internal!(@object $object [$($key)+] ($crate::plist_internal!(false)) $($rest)*);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Next value is an array.
|
|
||||||
(@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
|
|
||||||
$crate::plist_internal!(@object $object [$($key)+] ($crate::plist_internal!([$($array)*])) $($rest)*);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Next value is a map.
|
|
||||||
(@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
|
|
||||||
$crate::plist_internal!(@object $object [$($key)+] ($crate::plist_internal!({$($map)*})) $($rest)*);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Optional insert with trailing comma: key?: expr,
|
|
||||||
(@object $object:ident ($($key:tt)+) (:? $value:expr , $($rest:tt)*) $copy:tt) => {
|
|
||||||
if let Some(__v) = $crate::plist_macro::plist_maybe($value) {
|
|
||||||
let _ = $object.insert(($($key)+).into(), __v);
|
|
||||||
}
|
|
||||||
$crate::plist_internal!(@object $object () ($($rest)*) ($($rest)*));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Optional insert, last entry: key?: expr
|
|
||||||
(@object $object:ident ($($key:tt)+) (:? $value:expr) $copy:tt) => {
|
|
||||||
if let Some(__v) = $crate::plist_macro::plist_maybe($value) {
|
|
||||||
let _ = $object.insert(($($key)+).into(), __v);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
(@object $object:ident () ( :< $value:expr , $($rest:tt)*) $copy:tt) => {
|
|
||||||
{
|
|
||||||
let __v = $crate::plist_internal!($value);
|
|
||||||
let __dict = $crate::plist_macro::IntoPlistDict::into_plist_dict(__v);
|
|
||||||
for (__k, __val) in __dict {
|
|
||||||
let _ = $object.insert(__k, __val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$crate::plist_internal!(@object $object () ($($rest)*) ($($rest)*));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Merge: last entry `:< expr`
|
|
||||||
(@object $object:ident () ( :< $value:expr ) $copy:tt) => {
|
|
||||||
{
|
|
||||||
let __v = $crate::plist_internal!($value);
|
|
||||||
let __dict = $crate::plist_macro::IntoPlistDict::into_plist_dict(__v);
|
|
||||||
for (__k, __val) in __dict {
|
|
||||||
let _ = $object.insert(__k, __val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Optional merge: `:< ? expr,` — only merge if Some(...)
|
|
||||||
(@object $object:ident () ( :< ? $value:expr , $($rest:tt)*) $copy:tt) => {
|
|
||||||
if let Some(__dict) = $crate::plist_macro::maybe_into_dict($value) {
|
|
||||||
for (__k, __val) in __dict {
|
|
||||||
let _ = $object.insert(__k, __val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$crate::plist_internal!(@object $object () ($($rest)*) ($($rest)*));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Optional merge: last entry `:< ? expr`
|
|
||||||
(@object $object:ident () ( :< ? $value:expr ) $copy:tt) => {
|
|
||||||
if let Some(__dict) = $crate::plist_macro::maybe_into_dict($value) {
|
|
||||||
for (__k, __val) in __dict {
|
|
||||||
let _ = $object.insert(__k, __val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Next value is an expression followed by comma.
|
|
||||||
(@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
|
|
||||||
$crate::plist_internal!(@object $object [$($key)+] ($crate::plist_internal!($value)) , $($rest)*);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Last value is an expression with no trailing comma.
|
|
||||||
(@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
|
|
||||||
$crate::plist_internal!(@object $object [$($key)+] ($crate::plist_internal!($value)));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Missing value for last entry. Trigger a reasonable error message.
|
|
||||||
(@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
|
|
||||||
// "unexpected end of macro invocation"
|
|
||||||
$crate::plist_internal!();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Missing colon and value for last entry. Trigger a reasonable error
|
|
||||||
// message.
|
|
||||||
(@object $object:ident ($($key:tt)+) () $copy:tt) => {
|
|
||||||
// "unexpected end of macro invocation"
|
|
||||||
$crate::plist_internal!();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Misplaced colon. Trigger a reasonable error message.
|
|
||||||
(@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
|
|
||||||
// Takes no arguments so "no rules expected the token `:`".
|
|
||||||
$crate::plist_unexpected!($colon);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Found a comma inside a key. Trigger a reasonable error message.
|
|
||||||
(@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
|
|
||||||
// Takes no arguments so "no rules expected the token `,`".
|
|
||||||
$crate::plist_unexpected!($comma);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Key is fully parenthesized. This avoids clippy double_parens false
|
|
||||||
// positives because the parenthesization may be necessary here.
|
|
||||||
(@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
|
|
||||||
$crate::plist_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Refuse to absorb colon token into key expression.
|
|
||||||
(@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => {
|
|
||||||
$crate::plist_expect_expr_comma!($($unexpected)+);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Munch a token into the current key.
|
|
||||||
(@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
|
|
||||||
$crate::plist_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*));
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// The main implementation.
|
|
||||||
//
|
|
||||||
// Must be invoked as: plist_internal!($($plist)+)
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
(null) => {
|
|
||||||
plist::Value::String("".to_string()) // plist doesn't have null, use empty string or consider other representation
|
|
||||||
};
|
|
||||||
|
|
||||||
(true) => {
|
|
||||||
plist::Value::Boolean(true)
|
|
||||||
};
|
|
||||||
|
|
||||||
(false) => {
|
|
||||||
plist::Value::Boolean(false)
|
|
||||||
};
|
|
||||||
|
|
||||||
([]) => {
|
|
||||||
plist::Value::Array(vec![])
|
|
||||||
};
|
|
||||||
|
|
||||||
([ $($tt:tt)+ ]) => {
|
|
||||||
plist::Value::Array($crate::plist_internal!(@array [] $($tt)+))
|
|
||||||
};
|
|
||||||
|
|
||||||
({}) => {
|
|
||||||
plist::Value::Dictionary(plist::Dictionary::new())
|
|
||||||
};
|
|
||||||
|
|
||||||
({ $($tt:tt)+ }) => {
|
|
||||||
plist::Value::Dictionary({
|
|
||||||
let mut object = plist::Dictionary::new();
|
|
||||||
$crate::plist_internal!(@object object () ($($tt)+) ($($tt)+));
|
|
||||||
object
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
// Any type that can be converted to plist::Value: numbers, strings, variables etc.
|
|
||||||
// Must be below every other rule.
|
|
||||||
($other:expr) => {
|
|
||||||
$crate::plist_macro::plist_to_value($other)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
#[doc(hidden)]
|
|
||||||
macro_rules! plist_unexpected {
|
|
||||||
() => {};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
#[doc(hidden)]
|
|
||||||
macro_rules! plist_expect_expr_comma {
|
|
||||||
($e:expr , $($tt:tt)*) => {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to convert various types to plist::Value
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn plist_to_value<T: PlistConvertible>(value: T) -> plist::Value {
|
|
||||||
value.to_plist_value()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trait for types that can be converted to plist::Value
|
|
||||||
pub trait PlistConvertible {
|
|
||||||
fn to_plist_value(self) -> plist::Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementations for common types
|
|
||||||
impl PlistConvertible for plist::Value {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlistConvertible for String {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::String(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlistConvertible for &str {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::String(self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlistConvertible for i16 {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Integer(self.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlistConvertible for i32 {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Integer(self.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlistConvertible for i64 {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Integer(self.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlistConvertible for u16 {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Integer((self as i64).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlistConvertible for u32 {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Integer((self as i64).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlistConvertible for u64 {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Integer((self as i64).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlistConvertible for f32 {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Real(self as f64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlistConvertible for f64 {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Real(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlistConvertible for bool {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Boolean(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> PlistConvertible for std::borrow::Cow<'a, str> {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::String(self.into_owned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl PlistConvertible for Vec<u8> {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Data(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl PlistConvertible for &[u8] {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Data(self.to_vec())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl PlistConvertible for std::time::SystemTime {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Date(self.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: PlistConvertible> PlistConvertible for Vec<T> {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Array(self.into_iter().map(|item| item.to_plist_value()).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: PlistConvertible + Clone> PlistConvertible for &[T] {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Array(
|
|
||||||
self.iter()
|
|
||||||
.map(|item| item.clone().to_plist_value())
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: PlistConvertible + Clone, const N: usize> PlistConvertible for [T; N] {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Array(self.into_iter().map(|item| item.to_plist_value()).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: PlistConvertible + Clone, const N: usize> PlistConvertible for &[T; N] {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Array(
|
|
||||||
self.iter()
|
|
||||||
.map(|item| item.clone().to_plist_value())
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlistConvertible for plist::Dictionary {
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
plist::Value::Dictionary(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> PlistConvertible for std::collections::HashMap<K, V>
|
|
||||||
where
|
|
||||||
K: Into<String>,
|
|
||||||
V: PlistConvertible,
|
|
||||||
{
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
let mut dict = plist::Dictionary::new();
|
|
||||||
for (key, value) in self {
|
|
||||||
dict.insert(key.into(), value.to_plist_value());
|
|
||||||
}
|
|
||||||
plist::Value::Dictionary(dict)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> PlistConvertible for std::collections::BTreeMap<K, V>
|
|
||||||
where
|
|
||||||
K: Into<String>,
|
|
||||||
V: PlistConvertible,
|
|
||||||
{
|
|
||||||
fn to_plist_value(self) -> plist::Value {
|
|
||||||
let mut dict = plist::Dictionary::new();
|
|
||||||
for (key, value) in self {
|
|
||||||
dict.insert(key.into(), value.to_plist_value());
|
|
||||||
}
|
|
||||||
plist::Value::Dictionary(dict)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Treat plain T as Some(T) and Option<T> as-is.
|
|
||||||
pub trait MaybePlist {
|
|
||||||
fn into_option_value(self) -> Option<plist::Value>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: PlistConvertible> MaybePlist for T {
|
|
||||||
fn into_option_value(self) -> Option<plist::Value> {
|
|
||||||
Some(self.to_plist_value())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: PlistConvertible> MaybePlist for Option<T> {
|
|
||||||
fn into_option_value(self) -> Option<plist::Value> {
|
|
||||||
self.map(|v| v.to_plist_value())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn plist_maybe<T: MaybePlist>(v: T) -> Option<plist::Value> {
|
|
||||||
v.into_option_value()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert things into a Dictionary we can merge.
|
|
||||||
pub trait IntoPlistDict {
|
|
||||||
fn into_plist_dict(self) -> plist::Dictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoPlistDict for plist::Dictionary {
|
|
||||||
fn into_plist_dict(self) -> plist::Dictionary {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoPlistDict for plist::Value {
|
|
||||||
fn into_plist_dict(self) -> plist::Dictionary {
|
|
||||||
match self {
|
|
||||||
plist::Value::Dictionary(d) => d,
|
|
||||||
other => panic!("plist :< expects a dictionary, got {other:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> IntoPlistDict for std::collections::HashMap<K, V>
|
|
||||||
where
|
|
||||||
K: Into<String>,
|
|
||||||
V: PlistConvertible,
|
|
||||||
{
|
|
||||||
fn into_plist_dict(self) -> plist::Dictionary {
|
|
||||||
let mut d = plist::Dictionary::new();
|
|
||||||
for (k, v) in self {
|
|
||||||
d.insert(k.into(), v.to_plist_value());
|
|
||||||
}
|
|
||||||
d
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> IntoPlistDict for std::collections::BTreeMap<K, V>
|
|
||||||
where
|
|
||||||
K: Into<String>,
|
|
||||||
V: PlistConvertible,
|
|
||||||
{
|
|
||||||
fn into_plist_dict(self) -> plist::Dictionary {
|
|
||||||
let mut d = plist::Dictionary::new();
|
|
||||||
for (k, v) in self {
|
|
||||||
d.insert(k.into(), v.to_plist_value());
|
|
||||||
}
|
|
||||||
d
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional version: T or Option<T>.
|
|
||||||
pub trait MaybeIntoPlistDict {
|
|
||||||
fn into_option_plist_dict(self) -> Option<plist::Dictionary>;
|
|
||||||
}
|
|
||||||
impl<T: IntoPlistDict> MaybeIntoPlistDict for T {
|
|
||||||
fn into_option_plist_dict(self) -> Option<plist::Dictionary> {
|
|
||||||
Some(self.into_plist_dict())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: IntoPlistDict> MaybeIntoPlistDict for Option<T> {
|
|
||||||
fn into_option_plist_dict(self) -> Option<plist::Dictionary> {
|
|
||||||
self.map(|t| t.into_plist_dict())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn maybe_into_dict<T: MaybeIntoPlistDict>(v: T) -> Option<plist::Dictionary> {
|
|
||||||
v.into_option_plist_dict()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn test_plist_macro_basic() {
|
|
||||||
let value = plist!({
|
|
||||||
"name": "test",
|
|
||||||
"count": 42,
|
|
||||||
"active": true,
|
|
||||||
"items": ["a", ?"b", "c"]
|
|
||||||
});
|
|
||||||
|
|
||||||
if let plist::Value::Dictionary(dict) = value {
|
|
||||||
assert_eq!(
|
|
||||||
dict.get("name"),
|
|
||||||
Some(&plist::Value::String("test".to_string()))
|
|
||||||
);
|
|
||||||
assert_eq!(dict.get("count"), Some(&plist::Value::Integer(42.into())));
|
|
||||||
assert_eq!(dict.get("active"), Some(&plist::Value::Boolean(true)));
|
|
||||||
} else {
|
|
||||||
panic!("Expected dictionary");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_plist_macro_with_variables() {
|
|
||||||
let name = "dynamic";
|
|
||||||
let count = 100;
|
|
||||||
let items = vec!["x", "y"];
|
|
||||||
let none: Option<u64> = None;
|
|
||||||
|
|
||||||
let to_merge = plist!({
|
|
||||||
"reee": "cool beans"
|
|
||||||
});
|
|
||||||
let maybe_merge = Some(plist!({
|
|
||||||
"yeppers": "what did I say about yeppers",
|
|
||||||
"replace me": 2,
|
|
||||||
}));
|
|
||||||
let value = plist!({
|
|
||||||
"name": name,
|
|
||||||
"count": count,
|
|
||||||
"items": items,
|
|
||||||
"omit me":? none,
|
|
||||||
"keep me":? Some(123),
|
|
||||||
"replace me": 1,
|
|
||||||
:< to_merge,
|
|
||||||
:<? maybe_merge
|
|
||||||
});
|
|
||||||
|
|
||||||
if let plist::Value::Dictionary(dict) = value {
|
|
||||||
assert_eq!(
|
|
||||||
dict.get("name"),
|
|
||||||
Some(&plist::Value::String("dynamic".to_string()))
|
|
||||||
);
|
|
||||||
assert_eq!(dict.get("count"), Some(&plist::Value::Integer(100.into())));
|
|
||||||
assert!(dict.get("omit me").is_none());
|
|
||||||
assert_eq!(
|
|
||||||
dict.get("keep me"),
|
|
||||||
Some(&plist::Value::Integer(123.into()))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
dict.get("reee"),
|
|
||||||
Some(&plist::Value::String("cool beans".to_string()))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
dict.get("yeppers"),
|
|
||||||
Some(&plist::Value::String(
|
|
||||||
"what did I say about yeppers".to_string()
|
|
||||||
))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
dict.get("replace me"),
|
|
||||||
Some(&plist::Value::Integer(2.into()))
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
panic!("Expected dictionary");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,133 +1,160 @@
|
|||||||
// Jackson Coxson
|
// Jackson Coxson
|
||||||
|
|
||||||
use std::io::SeekFrom;
|
use std::{io::SeekFrom, marker::PhantomPinned, pin::Pin};
|
||||||
|
|
||||||
use crate::IdeviceError;
|
use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite};
|
||||||
|
|
||||||
use super::{
|
use crate::{
|
||||||
opcode::AfcOpcode,
|
IdeviceError,
|
||||||
packet::{AfcPacket, AfcPacketHeader},
|
afc::{
|
||||||
|
AfcClient,
|
||||||
|
inner_file::{InnerFileDescriptor, OwnedInnerFileDescriptor},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Maximum transfer size for file operations (64KB)
|
#[derive(Debug)]
|
||||||
const MAX_TRANSFER: u64 = 64 * 1024; // this is what go-ios uses
|
|
||||||
|
|
||||||
/// Handle for an open file on the device.
|
|
||||||
/// Call close before dropping
|
|
||||||
pub struct FileDescriptor<'a> {
|
pub struct FileDescriptor<'a> {
|
||||||
pub(crate) client: &'a mut super::AfcClient,
|
inner: Pin<Box<InnerFileDescriptor<'a>>>,
|
||||||
pub(crate) fd: u64,
|
|
||||||
pub(crate) path: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileDescriptor<'_> {
|
#[derive(Debug)]
|
||||||
/// Generic helper to send an AFC packet and read the response
|
pub struct OwnedFileDescriptor {
|
||||||
async fn send_packet(
|
inner: Pin<Box<OwnedInnerFileDescriptor>>,
|
||||||
&mut self,
|
}
|
||||||
opcode: AfcOpcode,
|
|
||||||
header_payload: Vec<u8>,
|
|
||||||
payload: Vec<u8>,
|
|
||||||
) -> Result<AfcPacket, IdeviceError> {
|
|
||||||
let header_len = header_payload.len() as u64 + AfcPacketHeader::LEN;
|
|
||||||
let header = AfcPacketHeader {
|
|
||||||
magic: super::MAGIC,
|
|
||||||
entire_len: header_len + payload.len() as u64,
|
|
||||||
header_payload_len: header_len,
|
|
||||||
packet_num: self.client.package_number,
|
|
||||||
operation: opcode,
|
|
||||||
};
|
|
||||||
self.client.package_number += 1;
|
|
||||||
|
|
||||||
let packet = AfcPacket {
|
impl<'a> FileDescriptor<'a> {
|
||||||
header,
|
/// create a new FileDescriptor from a raw fd
|
||||||
header_payload,
|
///
|
||||||
payload,
|
/// # Safety
|
||||||
};
|
/// make sure the fd is an opened file, and that you got it from a previous
|
||||||
|
/// FileDescriptor via `as_raw_fd()` method
|
||||||
|
pub unsafe fn new(client: &'a mut AfcClient, fd: u64, path: String) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Box::pin(InnerFileDescriptor {
|
||||||
|
client,
|
||||||
|
fd,
|
||||||
|
path,
|
||||||
|
pending_fut: None,
|
||||||
|
_m: PhantomPinned,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.client.send(packet).await?;
|
/// Closes the file descriptor
|
||||||
self.client.read().await
|
pub async fn close(self) -> Result<(), IdeviceError> {
|
||||||
|
self.inner.close().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnedFileDescriptor {
|
||||||
|
/// create a new OwnedFileDescriptor from a raw fd
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// make sure the fd is an opened file, and that you got it from a previous
|
||||||
|
/// OwnedFileDescriptor via `as_raw_fd()` method
|
||||||
|
pub unsafe fn new(client: AfcClient, fd: u64, path: String) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Box::pin(OwnedInnerFileDescriptor {
|
||||||
|
client,
|
||||||
|
fd,
|
||||||
|
path,
|
||||||
|
pending_fut: None,
|
||||||
|
_m: PhantomPinned,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Closes the file descriptor
|
||||||
|
pub async fn close(self) -> Result<AfcClient, IdeviceError> {
|
||||||
|
self.inner.close().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// gets the owned afc
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// this get's the afc out without closing, if you want to get the afc and close the file, use
|
||||||
|
/// `.close()`
|
||||||
|
pub unsafe fn get_inner_afc(self) -> AfcClient {
|
||||||
|
self.inner.get_inner_afc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::impl_to_structs!(FileDescriptor<'_>, OwnedFileDescriptor; {
|
||||||
|
pub fn as_raw_fd(&self) -> u64 {
|
||||||
|
self.inner.fd
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current cursor position for the file
|
/// Returns the current cursor position for the file
|
||||||
pub async fn seek_tell(&mut self) -> Result<u64, IdeviceError> {
|
pub async fn seek_tell(&mut self) -> Result<u64, IdeviceError> {
|
||||||
let header_payload = self.fd.to_le_bytes().to_vec();
|
self.inner.as_mut().seek_tell().await
|
||||||
let res = self
|
|
||||||
.send_packet(AfcOpcode::FileTell, header_payload, Vec::new())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let cur_pos = res
|
|
||||||
.header_payload
|
|
||||||
.get(..8)
|
|
||||||
.ok_or(IdeviceError::UnexpectedResponse)?
|
|
||||||
.try_into()
|
|
||||||
.map(u64::from_le_bytes)
|
|
||||||
.map_err(|_| IdeviceError::UnexpectedResponse)?;
|
|
||||||
|
|
||||||
Ok(cur_pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Moves the file cursor
|
|
||||||
pub async fn seek(&mut self, pos: SeekFrom) -> Result<(), IdeviceError> {
|
|
||||||
let (offset, whence) = match pos {
|
|
||||||
SeekFrom::Start(off) => (off as i64, 0),
|
|
||||||
SeekFrom::Current(off) => (off, 1),
|
|
||||||
SeekFrom::End(off) => (off, 2),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut header_payload = Vec::new();
|
|
||||||
header_payload.extend(self.fd.to_le_bytes());
|
|
||||||
header_payload.extend((whence as u64).to_le_bytes());
|
|
||||||
header_payload.extend(offset.to_le_bytes());
|
|
||||||
|
|
||||||
self.send_packet(AfcOpcode::FileSeek, header_payload, Vec::new())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Closes the file descriptor
|
|
||||||
pub async fn close(mut self) -> Result<(), IdeviceError> {
|
|
||||||
let header_payload = self.fd.to_le_bytes().to_vec();
|
|
||||||
|
|
||||||
self.send_packet(AfcOpcode::FileClose, header_payload, Vec::new())
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the entire contents of the file
|
/// Reads the entire contents of the file
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// A vector containing the file's data
|
/// A vector containing the file's data
|
||||||
pub async fn read(&mut self) -> Result<Vec<u8>, IdeviceError> {
|
pub async fn read_entire(&mut self) -> Result<Vec<u8>, IdeviceError> {
|
||||||
let seek_pos = self.seek_tell().await? as usize;
|
self.inner.as_mut().read().await
|
||||||
let file_info = self.client.get_file_info(&self.path).await?;
|
|
||||||
let mut bytes_left = file_info.size.saturating_sub(seek_pos);
|
|
||||||
let mut collected_bytes = Vec::with_capacity(bytes_left);
|
|
||||||
|
|
||||||
while bytes_left > 0 {
|
|
||||||
let mut header_payload = self.fd.to_le_bytes().to_vec();
|
|
||||||
header_payload.extend_from_slice(&MAX_TRANSFER.to_le_bytes());
|
|
||||||
let res = self
|
|
||||||
.send_packet(AfcOpcode::Read, header_payload, Vec::new())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
bytes_left -= res.payload.len();
|
|
||||||
collected_bytes.extend(res.payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(collected_bytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes data to the file
|
/// Writes data to the file
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `bytes` - Data to write to the file
|
/// * `bytes` - Data to write to the file
|
||||||
pub async fn write(&mut self, bytes: &[u8]) -> Result<(), IdeviceError> {
|
pub async fn write_entire(&mut self, bytes: &[u8]) -> Result<(), IdeviceError> {
|
||||||
for chunk in bytes.chunks(MAX_TRANSFER as usize) {
|
self.inner.as_mut().write(bytes).await
|
||||||
let header_payload = self.fd.to_le_bytes().to_vec();
|
|
||||||
self.send_packet(AfcOpcode::Write, header_payload, chunk.to_vec())
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
});
|
||||||
|
|
||||||
|
crate::impl_trait_to_structs!(AsyncRead for FileDescriptor<'_>, OwnedFileDescriptor; {
|
||||||
|
fn poll_read(
|
||||||
|
mut self: std::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
buf: &mut tokio::io::ReadBuf<'_>,
|
||||||
|
) -> std::task::Poll<std::io::Result<()>> {
|
||||||
|
let inner = self.inner.as_mut();
|
||||||
|
inner.poll_read(cx, buf)
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
crate::impl_trait_to_structs!(AsyncWrite for FileDescriptor<'_>, OwnedFileDescriptor; {
|
||||||
|
fn poll_write(
|
||||||
|
mut self: std::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> std::task::Poll<Result<usize, std::io::Error>> {
|
||||||
|
let inner = self.inner.as_mut();
|
||||||
|
inner.poll_write(cx, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn poll_flush(
|
||||||
|
mut self: std::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||||
|
let inner = self.inner.as_mut();
|
||||||
|
inner.poll_flush(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(
|
||||||
|
mut self: std::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||||
|
let inner = self.inner.as_mut();
|
||||||
|
inner.poll_shutdown(cx)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
crate::impl_trait_to_structs!(AsyncSeek for FileDescriptor<'_>, OwnedFileDescriptor; {
|
||||||
|
fn start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> std::io::Result<()> {
|
||||||
|
let this = self.inner.as_mut();
|
||||||
|
this.start_seek(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_complete(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
) -> std::task::Poll<std::io::Result<u64>> {
|
||||||
|
let this = self.inner.as_mut();
|
||||||
|
this.poll_complete(cx)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
818
idevice/src/services/afc/inner_file.rs
Normal file
818
idevice/src/services/afc/inner_file.rs
Normal file
@@ -0,0 +1,818 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
use std::{io::SeekFrom, pin::Pin};
|
||||||
|
|
||||||
|
use futures::{FutureExt, future::BoxFuture};
|
||||||
|
use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
IdeviceError,
|
||||||
|
afc::{
|
||||||
|
AfcClient, MAGIC,
|
||||||
|
opcode::AfcOpcode,
|
||||||
|
packet::{AfcPacket, AfcPacketHeader},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Maximum transfer size for file operations (1MB)
|
||||||
|
const MAX_TRANSFER: u64 = 1024 * 1024; // this is what libimobiledevice uses in it's afcclient
|
||||||
|
|
||||||
|
fn chunk_number(n: usize, chunk_size: usize) -> impl Iterator<Item = usize> {
|
||||||
|
(0..n)
|
||||||
|
.step_by(chunk_size)
|
||||||
|
.map(move |i| (n - i).min(chunk_size))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to descripe what the future returns
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum PendingResult {
|
||||||
|
// writing
|
||||||
|
Empty,
|
||||||
|
// seeking
|
||||||
|
SeekPos(u64),
|
||||||
|
// reading
|
||||||
|
Bytes(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
type OwnedBoxFuture = Pin<Box<dyn Future<Output = Result<PendingResult, IdeviceError>> + Send>>;
|
||||||
|
|
||||||
|
/// Handle for an open file on the device.
|
||||||
|
/// Call close before dropping
|
||||||
|
pub(crate) struct InnerFileDescriptor<'a> {
|
||||||
|
pub(crate) client: &'a mut AfcClient,
|
||||||
|
pub(crate) fd: u64,
|
||||||
|
pub(crate) path: String,
|
||||||
|
|
||||||
|
pub(crate) pending_fut: Option<BoxFuture<'a, Result<PendingResult, IdeviceError>>>,
|
||||||
|
pub(crate) _m: std::marker::PhantomPinned,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle for an owned open file on the device.
|
||||||
|
/// Call close before dropping
|
||||||
|
pub(crate) struct OwnedInnerFileDescriptor {
|
||||||
|
pub(crate) client: AfcClient,
|
||||||
|
pub(crate) fd: u64,
|
||||||
|
pub(crate) path: String,
|
||||||
|
|
||||||
|
pub(crate) pending_fut: Option<OwnedBoxFuture>,
|
||||||
|
pub(crate) _m: std::marker::PhantomPinned,
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::impl_to_structs!(InnerFileDescriptor<'_>, OwnedInnerFileDescriptor; {
|
||||||
|
/// Generic helper to send an AFC packet and read the response
|
||||||
|
pub async fn send_packet(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
opcode: AfcOpcode,
|
||||||
|
header_payload: Vec<u8>,
|
||||||
|
payload: Vec<u8>,
|
||||||
|
) -> Result<AfcPacket, IdeviceError> {
|
||||||
|
// SAFETY: we don't modify pinned fileds, it's ok
|
||||||
|
let this = unsafe { self.get_unchecked_mut() };
|
||||||
|
|
||||||
|
let header_len = header_payload.len() as u64 + AfcPacketHeader::LEN;
|
||||||
|
let header = AfcPacketHeader {
|
||||||
|
magic: MAGIC,
|
||||||
|
entire_len: header_len + payload.len() as u64,
|
||||||
|
header_payload_len: header_len,
|
||||||
|
packet_num: this.client.package_number,
|
||||||
|
operation: opcode,
|
||||||
|
};
|
||||||
|
this.client.package_number += 1;
|
||||||
|
|
||||||
|
let packet = AfcPacket {
|
||||||
|
header,
|
||||||
|
header_payload,
|
||||||
|
payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.client.send(packet).await?;
|
||||||
|
this.client.read().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current cursor position for the file
|
||||||
|
pub async fn seek_tell(self: Pin<&mut Self>) -> Result<u64, IdeviceError> {
|
||||||
|
let header_payload = self.fd.to_le_bytes().to_vec();
|
||||||
|
let res = self
|
||||||
|
.send_packet(AfcOpcode::FileTell, header_payload, Vec::new())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let cur_pos = res
|
||||||
|
.header_payload
|
||||||
|
.get(..8)
|
||||||
|
.ok_or(IdeviceError::UnexpectedResponse)?
|
||||||
|
.try_into()
|
||||||
|
.map(u64::from_le_bytes)
|
||||||
|
.map_err(|_| IdeviceError::UnexpectedResponse)?;
|
||||||
|
|
||||||
|
Ok(cur_pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the file cursor
|
||||||
|
async fn seek(mut self: Pin<&mut Self>, pos: SeekFrom) -> Result<u64, IdeviceError> {
|
||||||
|
let (offset, whence) = match pos {
|
||||||
|
SeekFrom::Start(off) => (off as i64, 0),
|
||||||
|
SeekFrom::Current(off) => (off, 1),
|
||||||
|
SeekFrom::End(off) => (off, 2),
|
||||||
|
};
|
||||||
|
|
||||||
|
let header_payload = [
|
||||||
|
self.fd.to_le_bytes(),
|
||||||
|
(whence as u64).to_le_bytes(),
|
||||||
|
offset.to_le_bytes(),
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
|
self.as_mut()
|
||||||
|
.send_packet(AfcOpcode::FileSeek, header_payload, Vec::new())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
self.as_mut().seek_tell().await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Reads n size of contents from the file
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `n` - amount of bytes to read
|
||||||
|
/// # Returns
|
||||||
|
/// A vector containing the file's data
|
||||||
|
pub async fn read_n(mut self: Pin<&mut Self>, n: usize) -> Result<Vec<u8>, IdeviceError> {
|
||||||
|
let mut collected_bytes = Vec::with_capacity(n);
|
||||||
|
|
||||||
|
for chunk in chunk_number(n, MAX_TRANSFER as usize) {
|
||||||
|
let header_payload = [self.fd.to_le_bytes(), (chunk as u64).to_le_bytes()].concat();
|
||||||
|
let res = self
|
||||||
|
.as_mut()
|
||||||
|
.send_packet(AfcOpcode::Read, header_payload, Vec::new())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
collected_bytes.extend(res.payload);
|
||||||
|
}
|
||||||
|
Ok(collected_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the entire contents of the file
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A vector containing the file's data
|
||||||
|
pub async fn read(mut self: Pin<&mut Self>) -> Result<Vec<u8>, IdeviceError> {
|
||||||
|
let seek_pos = self.as_mut().seek_tell().await? as usize;
|
||||||
|
|
||||||
|
let file_info = unsafe {
|
||||||
|
let this = self.as_mut().get_unchecked_mut();
|
||||||
|
|
||||||
|
this.client.get_file_info(&this.path).await?
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut bytes_left = file_info.size.saturating_sub(seek_pos);
|
||||||
|
let mut collected_bytes = Vec::with_capacity(bytes_left);
|
||||||
|
|
||||||
|
while bytes_left > 0 {
|
||||||
|
let bytes = self.as_mut().read_n(MAX_TRANSFER as usize).await?;
|
||||||
|
|
||||||
|
bytes_left -= bytes.len();
|
||||||
|
collected_bytes.extend(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(collected_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes data to the file
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `bytes` - Data to write to the file
|
||||||
|
pub async fn write(mut self: Pin<&mut Self>, bytes: &[u8]) -> Result<(), IdeviceError> {
|
||||||
|
for chunk in bytes.chunks(MAX_TRANSFER as usize) {
|
||||||
|
let header_payload = self.as_ref().fd.to_le_bytes().to_vec();
|
||||||
|
self.as_mut()
|
||||||
|
.send_packet(AfcOpcode::Write, header_payload, chunk.to_vec())
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store_pending_read(mut self: Pin<&mut Self>, buf_rem: usize) {
|
||||||
|
unsafe {
|
||||||
|
let this = self.as_mut().get_unchecked_mut() as *mut Self;
|
||||||
|
|
||||||
|
let fut = Some(
|
||||||
|
// SAFETY: we already know that self is pinned
|
||||||
|
Pin::new_unchecked(&mut *this)
|
||||||
|
.read_n(buf_rem)
|
||||||
|
.map(|r| r.map(PendingResult::Bytes))
|
||||||
|
.boxed(),
|
||||||
|
);
|
||||||
|
|
||||||
|
(&mut *this).pending_fut = fut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store_pending_seek(mut self: Pin<&mut Self>, position: std::io::SeekFrom) {
|
||||||
|
unsafe {
|
||||||
|
let this = self.as_mut().get_unchecked_mut() as *mut Self;
|
||||||
|
|
||||||
|
let fut = Some(
|
||||||
|
Pin::new_unchecked(&mut *this)
|
||||||
|
.seek(position)
|
||||||
|
.map(|r| r.map(PendingResult::SeekPos))
|
||||||
|
.boxed(),
|
||||||
|
);
|
||||||
|
|
||||||
|
(&mut *this).pending_fut = fut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store_pending_write(mut self: Pin<&mut Self>, buf: &'_ [u8]) {
|
||||||
|
unsafe {
|
||||||
|
let this = self.as_mut().get_unchecked_mut();
|
||||||
|
|
||||||
|
let this = this as *mut Self;
|
||||||
|
|
||||||
|
// move the entire buffer into the future so we don't have to store it somewhere
|
||||||
|
let pined_this = Pin::new_unchecked(&mut *this);
|
||||||
|
let buf = buf.to_vec();
|
||||||
|
let fut =
|
||||||
|
async move { pined_this.write(&buf).await.map(|_| PendingResult::Empty) }.boxed();
|
||||||
|
|
||||||
|
(&mut *this).pending_fut = Some(fut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
impl<'a> InnerFileDescriptor<'a> {
|
||||||
|
fn get_or_init_read_fut(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
buf_rem: usize,
|
||||||
|
) -> &mut BoxFuture<'a, Result<PendingResult, IdeviceError>> {
|
||||||
|
if self.as_ref().pending_fut.is_none() {
|
||||||
|
self.as_mut().store_pending_read(buf_rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { self.get_unchecked_mut().pending_fut.as_mut().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_or_init_write_fut(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
buf: &'_ [u8],
|
||||||
|
) -> &mut BoxFuture<'a, Result<PendingResult, IdeviceError>> {
|
||||||
|
if self.as_ref().pending_fut.is_none() {
|
||||||
|
self.as_mut().store_pending_write(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { self.get_unchecked_mut().pending_fut.as_mut().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_seek_fut(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
) -> Option<&mut BoxFuture<'a, Result<PendingResult, IdeviceError>>> {
|
||||||
|
unsafe { self.get_unchecked_mut().pending_fut.as_mut() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_pending_fut(mut self: Pin<&mut Self>) {
|
||||||
|
unsafe {
|
||||||
|
self.as_mut().get_unchecked_mut().pending_fut.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Closes the file descriptor
|
||||||
|
pub async fn close(mut self: Pin<Box<Self>>) -> Result<(), IdeviceError> {
|
||||||
|
let header_payload = self.fd.to_le_bytes().to_vec();
|
||||||
|
|
||||||
|
self.as_mut()
|
||||||
|
.send_packet(AfcOpcode::FileClose, header_payload, Vec::new())
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnedInnerFileDescriptor {
|
||||||
|
fn get_or_init_read_fut(mut self: Pin<&mut Self>, buf_rem: usize) -> &mut OwnedBoxFuture {
|
||||||
|
if self.as_ref().pending_fut.is_none() {
|
||||||
|
self.as_mut().store_pending_read(buf_rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { self.get_unchecked_mut().pending_fut.as_mut().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_or_init_write_fut(mut self: Pin<&mut Self>, buf: &'_ [u8]) -> &mut OwnedBoxFuture {
|
||||||
|
if self.as_ref().pending_fut.is_none() {
|
||||||
|
self.as_mut().store_pending_write(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { self.get_unchecked_mut().pending_fut.as_mut().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_seek_fut(self: Pin<&mut Self>) -> Option<&mut OwnedBoxFuture> {
|
||||||
|
unsafe { self.get_unchecked_mut().pending_fut.as_mut() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_pending_fut(mut self: Pin<&mut Self>) {
|
||||||
|
unsafe {
|
||||||
|
self.as_mut().get_unchecked_mut().pending_fut.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Closes the file descriptor
|
||||||
|
pub async fn close(mut self: Pin<Box<Self>>) -> Result<AfcClient, IdeviceError> {
|
||||||
|
let header_payload = self.fd.to_le_bytes().to_vec();
|
||||||
|
|
||||||
|
self.as_mut()
|
||||||
|
.send_packet(AfcOpcode::FileClose, header_payload, Vec::new())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// we don't need it to be pinned anymore
|
||||||
|
Ok(unsafe { Pin::into_inner_unchecked(self) }.client)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_inner_afc(self: Pin<Box<Self>>) -> AfcClient {
|
||||||
|
unsafe { Pin::into_inner_unchecked(self).client }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::impl_trait_to_structs!(AsyncRead for InnerFileDescriptor<'_>, OwnedInnerFileDescriptor; {
|
||||||
|
fn poll_read(
|
||||||
|
mut self: std::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
buf: &mut tokio::io::ReadBuf<'_>,
|
||||||
|
) -> std::task::Poll<std::io::Result<()>> {
|
||||||
|
let contents = {
|
||||||
|
let read_func = self.as_mut().get_or_init_read_fut(buf.remaining());
|
||||||
|
match std::task::ready!(read_func.as_mut().poll(cx)) {
|
||||||
|
Ok(PendingResult::Bytes(c)) => {
|
||||||
|
self.as_mut().remove_pending_fut();
|
||||||
|
c
|
||||||
|
}
|
||||||
|
Err(e) => return std::task::Poll::Ready(Err(std::io::Error::other(e.to_string()))),
|
||||||
|
|
||||||
|
_ => unreachable!("a non read future was stored, this shouldn't happen"),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
buf.put_slice(&contents);
|
||||||
|
|
||||||
|
std::task::Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
crate::impl_trait_to_structs!(AsyncWrite for InnerFileDescriptor<'_>, OwnedInnerFileDescriptor; {
|
||||||
|
fn poll_write(
|
||||||
|
mut self: std::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> std::task::Poll<Result<usize, std::io::Error>> {
|
||||||
|
let write_func = self.as_mut().get_or_init_write_fut(buf);
|
||||||
|
|
||||||
|
match std::task::ready!(write_func.as_mut().poll(cx)) {
|
||||||
|
Ok(PendingResult::Empty) => self.as_mut().remove_pending_fut(),
|
||||||
|
Err(e) => {
|
||||||
|
println!("error: {e}");
|
||||||
|
return std::task::Poll::Ready(Err(std::io::Error::other(e.to_string())));
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => unreachable!("a non write future was stored, this shouldn't happen"),
|
||||||
|
}
|
||||||
|
|
||||||
|
std::task::Poll::Ready(Ok(buf.len()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(
|
||||||
|
self: std::pin::Pin<&mut Self>,
|
||||||
|
_: &mut std::task::Context<'_>,
|
||||||
|
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||||
|
std::task::Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(
|
||||||
|
self: std::pin::Pin<&mut Self>,
|
||||||
|
_: &mut std::task::Context<'_>,
|
||||||
|
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||||
|
std::task::Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
crate::impl_trait_to_structs!(AsyncSeek for InnerFileDescriptor<'_>, OwnedInnerFileDescriptor; {
|
||||||
|
fn start_seek(self: Pin<&mut Self>, position: SeekFrom) -> std::io::Result<()> {
|
||||||
|
self.store_pending_seek(position);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_complete(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
) -> std::task::Poll<std::io::Result<u64>> {
|
||||||
|
let Some(fut) = self.as_mut().get_seek_fut() else {
|
||||||
|
// tokio runs the `poll_complete` before the `start_seek` to ensure no previous seek is in progress
|
||||||
|
return std::task::Poll::Ready(Ok(0));
|
||||||
|
};
|
||||||
|
|
||||||
|
match std::task::ready!(fut.as_mut().poll(cx)) {
|
||||||
|
Ok(PendingResult::SeekPos(pos)) => {
|
||||||
|
self.as_mut().remove_pending_fut();
|
||||||
|
std::task::Poll::Ready(Ok(pos))
|
||||||
|
}
|
||||||
|
Err(e) => std::task::Poll::Ready(Err(std::io::Error::other(e.to_string()))),
|
||||||
|
_ => unreachable!("a non seek future was stored, this shouldn't happen"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
impl std::fmt::Debug for InnerFileDescriptor<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("InnerFileDescriptor")
|
||||||
|
.field("client", &self.client)
|
||||||
|
.field("fd", &self.fd)
|
||||||
|
.field("path", &self.path)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for OwnedInnerFileDescriptor {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("OwnedInnerFileDescriptor")
|
||||||
|
.field("client", &self.client)
|
||||||
|
.field("fd", &self.fd)
|
||||||
|
.field("path", &self.path)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
IdeviceService as _,
|
||||||
|
afc::opcode::AfcFopenMode,
|
||||||
|
usbmuxd::{UsbmuxdAddr, UsbmuxdConnection},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
async fn make_client() -> AfcClient {
|
||||||
|
let mut u = UsbmuxdConnection::default()
|
||||||
|
.await
|
||||||
|
.expect("failed to connect to usbmuxd");
|
||||||
|
let d = u
|
||||||
|
.get_devices()
|
||||||
|
.await
|
||||||
|
.expect("no devices")
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.expect("no devices connected")
|
||||||
|
.to_provider(UsbmuxdAddr::default(), "idevice_afc_file_inner_tests");
|
||||||
|
|
||||||
|
let mut ac = AfcClient::connect(&d)
|
||||||
|
.await
|
||||||
|
.expect("failed to connect to afc");
|
||||||
|
ac.mk_dir("/tmp").await.unwrap();
|
||||||
|
ac
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn write_and_read_large_file() {
|
||||||
|
let mut client = make_client().await;
|
||||||
|
let path = "/tmp/large_file.txt";
|
||||||
|
let data = vec![b'x'; 10_000_000]; // 10mb
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut file = client.open(path, AfcFopenMode::WrOnly).await.unwrap();
|
||||||
|
file.write_all(&data).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = client.open(path, AfcFopenMode::RdOnly).await.unwrap();
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
file.read_to_end(&mut buf).await.unwrap();
|
||||||
|
assert_eq!(buf.len(), data.len());
|
||||||
|
|
||||||
|
drop(file);
|
||||||
|
client.remove(path).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[should_panic]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn panic_safety() {
|
||||||
|
let mut client = make_client().await;
|
||||||
|
client.list_dir("/invalid").await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn file_seek_and_append() {
|
||||||
|
let mut client = make_client().await;
|
||||||
|
let path = "/tmp/seek_append.txt";
|
||||||
|
|
||||||
|
let mut f = client.open(path, AfcFopenMode::WrOnly).await.unwrap();
|
||||||
|
f.write_all(b"start").await.unwrap();
|
||||||
|
f.seek(std::io::SeekFrom::Start(0)).await.unwrap();
|
||||||
|
f.write_all(b"over").await.unwrap();
|
||||||
|
drop(f);
|
||||||
|
|
||||||
|
let mut f = client.open(path, AfcFopenMode::RdOnly).await.unwrap();
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
f.read_to_end(&mut buf).await.unwrap();
|
||||||
|
assert_eq!(&buf, b"overt"); // “over” overwrites start
|
||||||
|
|
||||||
|
drop(f);
|
||||||
|
client.remove(path).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn borrow_check_works() {
|
||||||
|
let mut client = make_client().await;
|
||||||
|
let fut = client.list_dir("/Downloads");
|
||||||
|
// // This line should fail to compile if uncommented:
|
||||||
|
// let fut2 = client.list_dir("/bar");
|
||||||
|
fut.await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn not_send_across_threads() {
|
||||||
|
let mut client = make_client().await;
|
||||||
|
// // This should fail to compile if uncommented:
|
||||||
|
// tokio::spawn(async move { client.list_dir("/").await });
|
||||||
|
let _ = client.list_dir("/").await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn write_and_read_roundtrip() {
|
||||||
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
|
||||||
|
let mut client = make_client().await;
|
||||||
|
|
||||||
|
// Create a test file in /tmp (AFC should allow this)
|
||||||
|
let path = "/tmp/afc_test_file.txt";
|
||||||
|
let contents = b"hello async afc world";
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut file = client.open(path, AfcFopenMode::WrOnly).await.unwrap();
|
||||||
|
file.write_all(contents).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut file = client.open(path, AfcFopenMode::RdOnly).await.unwrap();
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
file.read_to_end(&mut buf).await.unwrap();
|
||||||
|
assert_eq!(buf, contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
client.remove(path).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn write_multiple_chunks() {
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
|
||||||
|
let mut client = make_client().await;
|
||||||
|
let path = "/tmp/afc_chunk_test.txt";
|
||||||
|
let mut file = client.open(path, AfcFopenMode::WrOnly).await.unwrap();
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
let data = format!("chunk{}\n", i);
|
||||||
|
file.write_all(data.as_bytes()).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(file);
|
||||||
|
|
||||||
|
let mut file = client.open(path, AfcFopenMode::RdOnly).await.unwrap();
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
tokio::io::AsyncReadExt::read_to_end(&mut file, &mut buf)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let s = String::from_utf8_lossy(&buf);
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
assert!(s.contains(&format!("chunk{}", i)));
|
||||||
|
}
|
||||||
|
drop(file);
|
||||||
|
|
||||||
|
client.remove(path).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn read_partial_and_resume() {
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
|
|
||||||
|
let mut client = make_client().await;
|
||||||
|
let path = "/tmp/afc_partial_read.txt";
|
||||||
|
let contents = b"abcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut file = client.open(path, AfcFopenMode::WrOnly).await.unwrap();
|
||||||
|
file.write_all(contents).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = client.open(path, AfcFopenMode::RdOnly).await.unwrap();
|
||||||
|
let mut buf = [0u8; 5];
|
||||||
|
let n = file.read(&mut buf).await.unwrap();
|
||||||
|
assert_eq!(&buf[..n], b"abcde");
|
||||||
|
|
||||||
|
let mut rest = Vec::new();
|
||||||
|
file.read_to_end(&mut rest).await.unwrap();
|
||||||
|
assert_eq!(rest, b"fghijklmnopqrstuvwxyz");
|
||||||
|
drop(file);
|
||||||
|
|
||||||
|
client.remove(path).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn zero_length_file() {
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
|
|
||||||
|
let mut client = make_client().await;
|
||||||
|
let path = "/tmp/afc_empty.txt";
|
||||||
|
|
||||||
|
{
|
||||||
|
let _ = client.open(path, AfcFopenMode::WrOnly).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = client.open(path, AfcFopenMode::RdOnly).await.unwrap();
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let n = file.read_to_end(&mut buf).await.unwrap();
|
||||||
|
assert_eq!(n, 0);
|
||||||
|
drop(file);
|
||||||
|
|
||||||
|
client.remove(path).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn write_then_append() {
|
||||||
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
|
||||||
|
let mut client = make_client().await;
|
||||||
|
let path = "/tmp/afc_append.txt";
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut file = client.open(path, AfcFopenMode::WrOnly).await.unwrap();
|
||||||
|
file.write_all(b"first\n").await.unwrap();
|
||||||
|
file.flush().await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut file = client.open(path, AfcFopenMode::Append).await.unwrap();
|
||||||
|
file.write_all(b"second\n").await.unwrap();
|
||||||
|
file.flush().await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = client.open(path, AfcFopenMode::RdOnly).await.unwrap();
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
file.read_to_end(&mut buf).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(String::from_utf8_lossy(&buf), "first\nsecond\n");
|
||||||
|
drop(file);
|
||||||
|
|
||||||
|
client.remove(path).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
||||||
|
async fn concurrent_file_access_should_not_ub() {
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::task;
|
||||||
|
|
||||||
|
let client = Arc::new(tokio::sync::Mutex::new(make_client().await));
|
||||||
|
let path = "/tmp/afc_threaded.txt";
|
||||||
|
|
||||||
|
let tasks: Vec<_> = (0..10)
|
||||||
|
.map(|i| {
|
||||||
|
let client = Arc::clone(&client);
|
||||||
|
task::spawn(async move {
|
||||||
|
let mut guard = client.lock().await;
|
||||||
|
let mut f = guard.open(path, AfcFopenMode::Append).await.unwrap();
|
||||||
|
f.write_all(format!("{}\n", i).as_bytes()).await.unwrap();
|
||||||
|
f.flush().await.unwrap();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for t in tasks {
|
||||||
|
let _ = t.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut guard = client.lock().await;
|
||||||
|
let mut f = guard.open(path, AfcFopenMode::RdOnly).await.unwrap();
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
tokio::io::AsyncReadExt::read_to_end(&mut f, &mut buf)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let s = String::from_utf8_lossy(&buf);
|
||||||
|
for i in 0..10 {
|
||||||
|
assert!(s.contains(&i.to_string()));
|
||||||
|
}
|
||||||
|
drop(f);
|
||||||
|
guard.remove(path).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn panic_during_write_does_not_leak() {
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
static COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
let mut client = make_client().await;
|
||||||
|
let path = "/tmp/afc_panic.txt";
|
||||||
|
|
||||||
|
let result = std::panic::AssertUnwindSafe(async {
|
||||||
|
let _f = client.open(path, AfcFopenMode::WrOnly).await.unwrap();
|
||||||
|
COUNT.fetch_add(1, Ordering::SeqCst);
|
||||||
|
panic!("simulate crash mid-write");
|
||||||
|
})
|
||||||
|
.catch_unwind()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
// Reopen to ensure no handles leaked
|
||||||
|
let _ = client.open(path, AfcFopenMode::WrOnly).await.unwrap();
|
||||||
|
assert_eq!(COUNT.load(Ordering::SeqCst), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn open_close_stress() {
|
||||||
|
let mut client = make_client().await;
|
||||||
|
let path = "/tmp/afc_stress.txt";
|
||||||
|
|
||||||
|
for _ in 0..100 {
|
||||||
|
let mut f = client.open(path, AfcFopenMode::WrOnly).await.unwrap();
|
||||||
|
f.write_all(b"hi").await.unwrap();
|
||||||
|
drop(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure handle cleanup didn’t break internal state
|
||||||
|
client.remove(path).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn concurrent_access_stress() {
|
||||||
|
let client = Arc::new(tokio::sync::Mutex::new(make_client().await));
|
||||||
|
|
||||||
|
let mut handles = vec![];
|
||||||
|
for i in 0..10 {
|
||||||
|
let client = client.clone();
|
||||||
|
handles.push(tokio::spawn(async move {
|
||||||
|
let mut client = client.lock().await;
|
||||||
|
let path = format!("/tmp/testfile_{}", i);
|
||||||
|
client.mk_dir(&path).await.ok();
|
||||||
|
let _ = client.list_dir("/tmp").await;
|
||||||
|
client.remove(&path).await.ok();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for h in handles {
|
||||||
|
let _ = h.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn read_write_mode_works() {
|
||||||
|
let mut client = make_client().await;
|
||||||
|
|
||||||
|
// Clean up from previous runs
|
||||||
|
let _ = client.remove("/tmp/rw_test.txt").await;
|
||||||
|
|
||||||
|
// Open for read/write
|
||||||
|
let mut file = client
|
||||||
|
.open("/tmp/rw_test.txt", AfcFopenMode::Rw)
|
||||||
|
.await
|
||||||
|
.expect("failed to open file in rw mode");
|
||||||
|
|
||||||
|
// Write some data
|
||||||
|
let data = b"hello world";
|
||||||
|
file.write_all(data).await.expect("failed to write");
|
||||||
|
|
||||||
|
// Seek back to start
|
||||||
|
file.seek(std::io::SeekFrom::Start(0))
|
||||||
|
.await
|
||||||
|
.expect("seek failed");
|
||||||
|
|
||||||
|
// Read it back
|
||||||
|
let mut buf = vec![0u8; data.len()];
|
||||||
|
file.read_exact(&mut buf).await.expect("failed to read");
|
||||||
|
assert_eq!(&buf, data);
|
||||||
|
|
||||||
|
// Write again at end
|
||||||
|
file.seek(std::io::SeekFrom::End(0)).await.unwrap();
|
||||||
|
file.write_all(b"!").await.unwrap();
|
||||||
|
|
||||||
|
// Verify new content
|
||||||
|
file.seek(std::io::SeekFrom::Start(0)).await.unwrap();
|
||||||
|
let mut final_buf = Vec::new();
|
||||||
|
file.read_to_end(&mut final_buf).await.unwrap();
|
||||||
|
assert_eq!(&final_buf, b"hello world!");
|
||||||
|
|
||||||
|
file.close().await.expect("failed to close");
|
||||||
|
|
||||||
|
// Double check via list/read
|
||||||
|
let contents = client
|
||||||
|
.open("/tmp/rw_test.txt", AfcFopenMode::RdOnly)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.read_entire()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(contents, b"hello world!");
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
client.remove("/tmp/rw_test.txt").await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
23
idevice/src/services/afc/inner_file_impl_macro.rs
Normal file
23
idevice/src/services/afc/inner_file_impl_macro.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! impl_to_structs {
|
||||||
|
(
|
||||||
|
$( $name:ident $(<$li:lifetime>)? ),+;
|
||||||
|
$body:tt
|
||||||
|
) => {
|
||||||
|
$(
|
||||||
|
impl $name $(<$li>)? $body
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! impl_trait_to_structs {
|
||||||
|
(
|
||||||
|
$trit:ident for $( $name:ident $(<$li:lifetime>)? ),+;
|
||||||
|
$body:tt
|
||||||
|
) => {
|
||||||
|
$(
|
||||||
|
impl $trit for $name $(<$li>)? $body
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -6,15 +6,21 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use errors::AfcError;
|
use errors::AfcError;
|
||||||
use file::FileDescriptor;
|
|
||||||
use log::warn;
|
|
||||||
use opcode::{AfcFopenMode, AfcOpcode};
|
use opcode::{AfcFopenMode, AfcOpcode};
|
||||||
use packet::{AfcPacket, AfcPacketHeader};
|
use packet::{AfcPacket, AfcPacketHeader};
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{Idevice, IdeviceError, IdeviceService, obf};
|
use crate::{
|
||||||
|
Idevice, IdeviceError, IdeviceService,
|
||||||
|
afc::file::{FileDescriptor, OwnedFileDescriptor},
|
||||||
|
lockdown::LockdownClient,
|
||||||
|
obf,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
|
mod inner_file;
|
||||||
|
mod inner_file_impl_macro;
|
||||||
pub mod opcode;
|
pub mod opcode;
|
||||||
pub mod packet;
|
pub mod packet;
|
||||||
|
|
||||||
@@ -22,6 +28,7 @@ pub mod packet;
|
|||||||
pub const MAGIC: u64 = 0x4141504c36414643;
|
pub const MAGIC: u64 = 0x4141504c36414643;
|
||||||
|
|
||||||
/// Client for interacting with the AFC service on iOS devices
|
/// Client for interacting with the AFC service on iOS devices
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct AfcClient {
|
pub struct AfcClient {
|
||||||
/// The underlying iDevice connection
|
/// The underlying iDevice connection
|
||||||
pub idevice: Idevice,
|
pub idevice: Idevice,
|
||||||
@@ -85,6 +92,43 @@ impl AfcClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Connects to afc2 from a provider
|
||||||
|
pub async fn new_afc2(
|
||||||
|
provider: &dyn crate::provider::IdeviceProvider,
|
||||||
|
) -> Result<Self, IdeviceError> {
|
||||||
|
let mut lockdown = LockdownClient::connect(provider).await?;
|
||||||
|
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
let legacy = lockdown
|
||||||
|
.get_value(Some("ProductVersion"), None)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|x| x.as_string())
|
||||||
|
.and_then(|x| x.split(".").next())
|
||||||
|
.and_then(|x| x.parse::<u8>().ok())
|
||||||
|
.map(|x| x < 5)
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "openssl"))]
|
||||||
|
let legacy = false;
|
||||||
|
|
||||||
|
lockdown
|
||||||
|
.start_session(&provider.get_pairing_file().await?)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let (port, ssl) = lockdown.start_service(obf!("com.apple.afc2")).await?;
|
||||||
|
|
||||||
|
let mut idevice = provider.connect(port).await?;
|
||||||
|
if ssl {
|
||||||
|
idevice
|
||||||
|
.start_session(&provider.get_pairing_file().await?, legacy)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::from_stream(idevice).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Lists the contents of a directory on the device
|
/// Lists the contents of a directory on the device
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -408,11 +452,54 @@ impl AfcClient {
|
|||||||
return Err(IdeviceError::UnexpectedResponse);
|
return Err(IdeviceError::UnexpectedResponse);
|
||||||
}
|
}
|
||||||
let fd = u64::from_le_bytes(res.header_payload[..8].try_into().unwrap());
|
let fd = u64::from_le_bytes(res.header_payload[..8].try_into().unwrap());
|
||||||
Ok(FileDescriptor {
|
|
||||||
client: self,
|
// we know it's a valid fd
|
||||||
fd,
|
Ok(unsafe { FileDescriptor::new(self, fd, path) })
|
||||||
path,
|
}
|
||||||
})
|
|
||||||
|
/// Opens an owned file on the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `path` - Path to the file to open
|
||||||
|
/// * `mode` - Opening mode (read, write, etc.)
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A `OwnedFileDescriptor` struct for the opened file
|
||||||
|
pub async fn open_owned(
|
||||||
|
mut self,
|
||||||
|
path: impl Into<String>,
|
||||||
|
mode: AfcFopenMode,
|
||||||
|
) -> Result<OwnedFileDescriptor, IdeviceError> {
|
||||||
|
let path = path.into();
|
||||||
|
let mut header_payload = (mode as u64).to_le_bytes().to_vec();
|
||||||
|
header_payload.extend(path.as_bytes());
|
||||||
|
let header_len = header_payload.len() as u64 + AfcPacketHeader::LEN;
|
||||||
|
|
||||||
|
let header = AfcPacketHeader {
|
||||||
|
magic: MAGIC,
|
||||||
|
entire_len: header_len, // it's the same since the payload is empty for this
|
||||||
|
header_payload_len: header_len,
|
||||||
|
packet_num: self.package_number,
|
||||||
|
operation: AfcOpcode::FileOpen,
|
||||||
|
};
|
||||||
|
self.package_number += 1;
|
||||||
|
|
||||||
|
let packet = AfcPacket {
|
||||||
|
header,
|
||||||
|
header_payload,
|
||||||
|
payload: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.send(packet).await?;
|
||||||
|
let res = self.read().await?;
|
||||||
|
if res.header_payload.len() < 8 {
|
||||||
|
warn!("Header payload fd is less than 8 bytes");
|
||||||
|
return Err(IdeviceError::UnexpectedResponse);
|
||||||
|
}
|
||||||
|
let fd = u64::from_le_bytes(res.header_payload[..8].try_into().unwrap());
|
||||||
|
|
||||||
|
// we know it's a valid fd
|
||||||
|
Ok(unsafe { OwnedFileDescriptor::new(self, fd, path) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a hard or symbolic link
|
/// Creates a hard or symbolic link
|
||||||
@@ -508,7 +595,7 @@ impl AfcClient {
|
|||||||
let res = AfcPacket::read(&mut self.idevice).await?;
|
let res = AfcPacket::read(&mut self.idevice).await?;
|
||||||
if res.header.operation == AfcOpcode::Status {
|
if res.header.operation == AfcOpcode::Status {
|
||||||
if res.header_payload.len() < 8 {
|
if res.header_payload.len() < 8 {
|
||||||
log::error!("AFC returned error opcode, but not a code");
|
tracing::error!("AFC returned error opcode, but not a code");
|
||||||
return Err(IdeviceError::UnexpectedResponse);
|
return Err(IdeviceError::UnexpectedResponse);
|
||||||
}
|
}
|
||||||
let code = u64::from_le_bytes(res.header_payload[..8].try_into().unwrap());
|
let code = u64::from_le_bytes(res.header_payload[..8].try_into().unwrap());
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Jackson Coxson
|
// Jackson Coxson
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
pub enum AfcOpcode {
|
pub enum AfcOpcode {
|
||||||
Status = 0x00000001,
|
Status = 0x00000001,
|
||||||
@@ -36,6 +36,7 @@ pub enum AfcOpcode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum AfcFopenMode {
|
pub enum AfcFopenMode {
|
||||||
RdOnly = 0x00000001, // r O_RDONLY
|
RdOnly = 0x00000001, // r O_RDONLY
|
||||||
Rw = 0x00000002, // r+ O_RDWR | O_CREAT
|
Rw = 0x00000002, // r+ O_RDWR | O_CREAT
|
||||||
@@ -46,6 +47,7 @@ pub enum AfcFopenMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum LinkType {
|
pub enum LinkType {
|
||||||
Hardlink = 0x00000001,
|
Hardlink = 0x00000001,
|
||||||
Symlink = 0x00000002,
|
Symlink = 0x00000002,
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// Jackson Coxson
|
// Jackson Coxson
|
||||||
|
|
||||||
use log::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::{Idevice, IdeviceError};
|
use crate::{Idevice, IdeviceError};
|
||||||
|
|
||||||
use super::opcode::AfcOpcode;
|
use super::opcode::AfcOpcode;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct AfcPacketHeader {
|
pub struct AfcPacketHeader {
|
||||||
pub magic: u64,
|
pub magic: u64,
|
||||||
pub entire_len: u64,
|
pub entire_len: u64,
|
||||||
@@ -32,7 +32,7 @@ impl AfcPacketHeader {
|
|||||||
res.extend_from_slice(&self.entire_len.to_le_bytes());
|
res.extend_from_slice(&self.entire_len.to_le_bytes());
|
||||||
res.extend_from_slice(&self.header_payload_len.to_le_bytes());
|
res.extend_from_slice(&self.header_payload_len.to_le_bytes());
|
||||||
res.extend_from_slice(&self.packet_num.to_le_bytes());
|
res.extend_from_slice(&self.packet_num.to_le_bytes());
|
||||||
res.extend_from_slice(&(self.operation.clone() as u64).to_le_bytes());
|
res.extend_from_slice(&(self.operation as u64).to_le_bytes());
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use crate::{Idevice, IdeviceError, IdeviceService, obf};
|
use crate::{Idevice, IdeviceError, IdeviceService, obf};
|
||||||
|
|
||||||
/// Client for interacting with the AMFI service on the device
|
/// Client for interacting with the AMFI service on the device
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct AmfiClient {
|
pub struct AmfiClient {
|
||||||
/// The underlying device connection with established amfi service
|
/// The underlying device connection with established amfi service
|
||||||
pub idevice: Idevice,
|
pub idevice: Idevice,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use log::{debug, warn};
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{Idevice, IdeviceError, IdeviceService, obf};
|
use crate::{Idevice, IdeviceError, IdeviceService, obf};
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ use crate::{Idevice, IdeviceError, IdeviceService, obf};
|
|||||||
/// You must have the Bluetooth profile installed, or you'll get no data.
|
/// You must have the Bluetooth profile installed, or you'll get no data.
|
||||||
///
|
///
|
||||||
/// ``https://developer.apple.com/bug-reporting/profiles-and-logs/?name=bluetooth``
|
/// ``https://developer.apple.com/bug-reporting/profiles-and-logs/?name=bluetooth``
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct BtPacketLoggerClient {
|
pub struct BtPacketLoggerClient {
|
||||||
/// The underlying device connection with established logger service
|
/// The underlying device connection with established logger service
|
||||||
pub idevice: Idevice,
|
pub idevice: Idevice,
|
||||||
@@ -110,16 +111,6 @@ impl BtPacketLoggerClient {
|
|||||||
let kind = BtPacketKind::from_byte(frame[off]);
|
let kind = BtPacketKind::from_byte(frame[off]);
|
||||||
let payload = &frame[off + 1..]; // whatever remains
|
let payload = &frame[off + 1..]; // whatever remains
|
||||||
|
|
||||||
// Optional soft check of advisory header.length
|
|
||||||
let advisory = hdr.length as usize;
|
|
||||||
let actual = 1 + payload.len(); // kind + payload
|
|
||||||
if advisory != actual {
|
|
||||||
debug!(
|
|
||||||
"BTPacketLogger advisory length {} != actual {}, proceeding",
|
|
||||||
advisory, actual
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build H4 buffer (prepend type byte)
|
// Build H4 buffer (prepend type byte)
|
||||||
let mut h4 = Vec::with_capacity(1 + payload.len());
|
let mut h4 = Vec::with_capacity(1 + payload.len());
|
||||||
if let Some(t) = kind.h4_type() {
|
if let Some(t) = kind.h4_type() {
|
||||||
@@ -158,13 +149,6 @@ impl BtPacketLoggerClient {
|
|||||||
let kind = BtPacketKind::from_byte(frame[off]);
|
let kind = BtPacketKind::from_byte(frame[off]);
|
||||||
let payload = &frame[off + 1..];
|
let payload = &frame[off + 1..];
|
||||||
|
|
||||||
// soft advisory check
|
|
||||||
let advisory = hdr.length as usize;
|
|
||||||
let actual = 1 + payload.len();
|
|
||||||
if advisory != actual {
|
|
||||||
debug!("BTPacketLogger advisory length {} != actual {}", advisory, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
// make H4 buffer
|
// make H4 buffer
|
||||||
let mut h4 = Vec::with_capacity(1 + payload.len());
|
let mut h4 = Vec::with_capacity(1 + payload.len());
|
||||||
if let Some(t) = kind.h4_type() {
|
if let Some(t) = kind.h4_type() {
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
//! Companion Proxy is Apple's bridge to connect to the Apple Watch
|
//! Companion Proxy is Apple's bridge to connect to the Apple Watch
|
||||||
|
|
||||||
use log::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{Idevice, IdeviceError, IdeviceService, RsdService, obf};
|
use crate::{Idevice, IdeviceError, IdeviceService, RsdService, obf};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct CompanionProxy {
|
pub struct CompanionProxy {
|
||||||
idevice: Idevice,
|
idevice: Idevice,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct CompanionProxyStream {
|
pub struct CompanionProxyStream {
|
||||||
proxy: CompanionProxy,
|
proxy: CompanionProxy,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// Jackson Coxson
|
// Jackson Coxson
|
||||||
|
|
||||||
use log::warn;
|
use plist_macro::plist_to_xml_bytes;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{IdeviceError, ReadWrite, RsdService, obf, xpc::XPCObject};
|
use crate::{IdeviceError, ReadWrite, RsdService, obf, xpc::XPCObject};
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ impl RsdService for AppServiceClient<Box<dyn ReadWrite>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct AppServiceClient<R: ReadWrite> {
|
pub struct AppServiceClient<R: ReadWrite> {
|
||||||
inner: CoreDeviceServiceClient<R>,
|
inner: CoreDeviceServiceClient<R>,
|
||||||
}
|
}
|
||||||
@@ -215,7 +217,7 @@ impl<R: ReadWrite> AppServiceClient<R> {
|
|||||||
"user": {
|
"user": {
|
||||||
"active": true,
|
"active": true,
|
||||||
},
|
},
|
||||||
"platformSpecificOptions": plist::Value::Data(crate::util::plist_to_xml_bytes(&platform_options.unwrap_or_default())),
|
"platformSpecificOptions": plist::Value::Data(plist_to_xml_bytes(&platform_options.unwrap_or_default())),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use log::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{IdeviceError, ReadWrite, RsdService, obf};
|
use crate::{IdeviceError, ReadWrite, RsdService, obf};
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ impl RsdService for DiagnostisServiceClient<Box<dyn ReadWrite>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct DiagnostisServiceClient<R: ReadWrite> {
|
pub struct DiagnostisServiceClient<R: ReadWrite> {
|
||||||
inner: super::CoreDeviceServiceClient<R>,
|
inner: super::CoreDeviceServiceClient<R>,
|
||||||
}
|
}
|
||||||
@@ -70,3 +71,12 @@ impl<R: ReadWrite> DiagnostisServiceClient<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for SysdiagnoseResponse<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("SysdiagnoseResponse")
|
||||||
|
.field("preferred_filename", &self.preferred_filename)
|
||||||
|
.field("expected_length", &self.expected_length)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Jackson Coxson
|
// Jackson Coxson
|
||||||
// Ported from pymobiledevice3
|
// Ported from pymobiledevice3
|
||||||
|
|
||||||
use log::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
IdeviceError, ReadWrite, RemoteXpcClient,
|
IdeviceError, ReadWrite, RemoteXpcClient,
|
||||||
@@ -17,6 +17,7 @@ pub use openstdiosocket::*;
|
|||||||
|
|
||||||
const CORE_SERVICE_VERSION: &str = "443.18";
|
const CORE_SERVICE_VERSION: &str = "443.18";
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct CoreDeviceServiceClient<R: ReadWrite> {
|
pub struct CoreDeviceServiceClient<R: ReadWrite> {
|
||||||
inner: RemoteXpcClient<R>,
|
inner: RemoteXpcClient<R>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ impl RsdService for OpenStdioSocketClient {
|
|||||||
|
|
||||||
/// Call ``read_uuid`` to get the UUID. Pass that to app service launch to connect to the stream of
|
/// Call ``read_uuid`` to get the UUID. Pass that to app service launch to connect to the stream of
|
||||||
/// the launched app. Inner is exposed to read and write to, using Tokio's AsyncReadExt/AsyncWriteExt
|
/// the launched app. Inner is exposed to read and write to, using Tokio's AsyncReadExt/AsyncWriteExt
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct OpenStdioSocketClient {
|
pub struct OpenStdioSocketClient {
|
||||||
pub inner: Box<dyn ReadWrite>,
|
pub inner: Box<dyn ReadWrite>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use crate::{Idevice, IdeviceError, IdeviceService, obf};
|
|||||||
|
|
||||||
use byteorder::{BigEndian, WriteBytesExt};
|
use byteorder::{BigEndian, WriteBytesExt};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::io::{self, Write};
|
use std::io::{self, IoSlice, Write};
|
||||||
|
|
||||||
/// A representation of a CDTunnel packet used in the CoreDeviceProxy protocol.
|
/// A representation of a CDTunnel packet used in the CoreDeviceProxy protocol.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@@ -82,6 +82,7 @@ impl CDTunnelPacket {
|
|||||||
/// A high-level client for the `com.apple.internal.devicecompute.CoreDeviceProxy` service.
|
/// A high-level client for the `com.apple.internal.devicecompute.CoreDeviceProxy` service.
|
||||||
///
|
///
|
||||||
/// Handles session negotiation, handshake, and tunnel communication.
|
/// Handles session negotiation, handshake, and tunnel communication.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct CoreDeviceProxy {
|
pub struct CoreDeviceProxy {
|
||||||
/// The underlying idevice connection used for communication.
|
/// The underlying idevice connection used for communication.
|
||||||
pub idevice: Idevice,
|
pub idevice: Idevice,
|
||||||
@@ -194,6 +195,20 @@ impl CoreDeviceProxy {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends a raw data packet through the tunnel using vectored I/O.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `bufs` - The buffers to send.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(())` if the data is successfully sent.
|
||||||
|
/// * `Err(IdeviceError)` if sending fails.
|
||||||
|
pub async fn send_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<(), IdeviceError> {
|
||||||
|
self.idevice.send_raw_vectored(bufs).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Receives up to `mtu` bytes from the tunnel.
|
/// Receives up to `mtu` bytes from the tunnel.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
//! function to trigger a flush of crash logs from system storage into the
|
//! function to trigger a flush of crash logs from system storage into the
|
||||||
//! crash reports directory by connecting to the `com.apple.crashreportmover` service.
|
//! crash reports directory by connecting to the `com.apple.crashreportmover` service.
|
||||||
|
|
||||||
use log::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use crate::{Idevice, IdeviceError, IdeviceService, afc::AfcClient, lockdown::LockdownClient, obf};
|
use crate::{Idevice, IdeviceError, IdeviceService, afc::AfcClient, lockdown::LockdownClient, obf};
|
||||||
|
|
||||||
@@ -15,6 +15,7 @@ use crate::{Idevice, IdeviceError, IdeviceService, afc::AfcClient, lockdown::Loc
|
|||||||
///
|
///
|
||||||
/// This client wraps access to the `com.apple.crashreportcopymobile` service,
|
/// This client wraps access to the `com.apple.crashreportcopymobile` service,
|
||||||
/// which exposes crash logs through the Apple File Conduit (AFC).
|
/// which exposes crash logs through the Apple File Conduit (AFC).
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct CrashReportCopyMobileClient {
|
pub struct CrashReportCopyMobileClient {
|
||||||
/// The underlying AFC client connected to the crash logs directory.
|
/// The underlying AFC client connected to the crash logs directory.
|
||||||
pub afc_client: AfcClient,
|
pub afc_client: AfcClient,
|
||||||
@@ -84,7 +85,7 @@ impl CrashReportCopyMobileClient {
|
|||||||
.open(format!("/{log}"), crate::afc::opcode::AfcFopenMode::RdOnly)
|
.open(format!("/{log}"), crate::afc::opcode::AfcFopenMode::RdOnly)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
f.read().await
|
f.read_entire().await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a specified crash log file from the device.
|
/// Removes a specified crash log file from the device.
|
||||||
@@ -123,6 +124,18 @@ pub async fn flush_reports(
|
|||||||
provider: &dyn crate::provider::IdeviceProvider,
|
provider: &dyn crate::provider::IdeviceProvider,
|
||||||
) -> Result<(), IdeviceError> {
|
) -> Result<(), IdeviceError> {
|
||||||
let mut lockdown = LockdownClient::connect(provider).await?;
|
let mut lockdown = LockdownClient::connect(provider).await?;
|
||||||
|
|
||||||
|
let legacy = lockdown
|
||||||
|
.get_value(Some("ProductVersion"), None)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|x| x.as_string())
|
||||||
|
.and_then(|x| x.split(".").next())
|
||||||
|
.and_then(|x| x.parse::<u8>().ok())
|
||||||
|
.map(|x| x < 5)
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
lockdown
|
lockdown
|
||||||
.start_session(&provider.get_pairing_file().await?)
|
.start_session(&provider.get_pairing_file().await?)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -134,7 +147,7 @@ pub async fn flush_reports(
|
|||||||
let mut idevice = provider.connect(port).await?;
|
let mut idevice = provider.connect(port).await?;
|
||||||
if ssl {
|
if ssl {
|
||||||
idevice
|
idevice
|
||||||
.start_session(&provider.get_pairing_file().await?)
|
.start_session(&provider.get_pairing_file().await?, legacy)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
//! GDB Remote Serial Protocol as documented at:
|
//! GDB Remote Serial Protocol as documented at:
|
||||||
//! https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html#Packets
|
//! https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html#Packets
|
||||||
|
|
||||||
use log::debug;
|
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::{IdeviceError, ReadWrite, RsdService, obf};
|
use crate::{IdeviceError, ReadWrite, RsdService, obf};
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@ impl RsdService for DebugProxyClient<Box<dyn ReadWrite>> {
|
|||||||
///
|
///
|
||||||
/// Implements the GDB Remote Serial Protocol for communicating with debugserver
|
/// Implements the GDB Remote Serial Protocol for communicating with debugserver
|
||||||
/// on iOS devices. Handles packet formatting, checksums, and acknowledgments.
|
/// on iOS devices. Handles packet formatting, checksums, and acknowledgments.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct DebugProxyClient<R: ReadWrite> {
|
pub struct DebugProxyClient<R: ReadWrite> {
|
||||||
/// The underlying socket connection to debugproxy
|
/// The underlying socket connection to debugproxy
|
||||||
pub socket: R,
|
pub socket: R,
|
||||||
@@ -38,6 +39,7 @@ pub struct DebugProxyClient<R: ReadWrite> {
|
|||||||
///
|
///
|
||||||
/// Commands follow the GDB Remote Serial Protocol format:
|
/// Commands follow the GDB Remote Serial Protocol format:
|
||||||
/// $<command>[<hex-encoded args>]#<checksum>
|
/// $<command>[<hex-encoded args>]#<checksum>
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct DebugserverCommand {
|
pub struct DebugserverCommand {
|
||||||
/// The command name (e.g. "qSupported", "vCont")
|
/// The command name (e.g. "qSupported", "vCont")
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use crate::{Idevice, IdeviceError, IdeviceService, obf};
|
use crate::{Idevice, IdeviceError, IdeviceService, obf};
|
||||||
|
|
||||||
/// Client for interacting with the Diagnostics Relay
|
/// Client for interacting with the Diagnostics Relay
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct DiagnosticsRelayClient {
|
pub struct DiagnosticsRelayClient {
|
||||||
/// The underlying device connection with established service
|
/// The underlying device connection with established service
|
||||||
pub idevice: Idevice,
|
pub idevice: Idevice,
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// A client for the location simulation service
|
/// A client for the location simulation service
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct LocationSimulationClient<'a, R: ReadWrite> {
|
pub struct LocationSimulationClient<'a, R: ReadWrite> {
|
||||||
/// The underlying channel used for communication
|
/// The underlying channel used for communication
|
||||||
channel: Channel<'a, R>,
|
channel: Channel<'a, R>,
|
||||||
|
|||||||
@@ -61,12 +61,12 @@
|
|||||||
use plist::Value;
|
use plist::Value;
|
||||||
use tokio::io::{AsyncRead, AsyncReadExt};
|
use tokio::io::{AsyncRead, AsyncReadExt};
|
||||||
|
|
||||||
use crate::IdeviceError;
|
use crate::{IdeviceError, pretty_print_plist};
|
||||||
|
|
||||||
/// Message header containing metadata about the message
|
/// Message header containing metadata about the message
|
||||||
///
|
///
|
||||||
/// 32-byte structure that appears at the start of every message
|
/// 32-byte structure that appears at the start of every message
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub struct MessageHeader {
|
pub struct MessageHeader {
|
||||||
/// Magic number identifying the protocol (0x1F3D5B79)
|
/// Magic number identifying the protocol (0x1F3D5B79)
|
||||||
magic: u32,
|
magic: u32,
|
||||||
@@ -91,7 +91,7 @@ pub struct MessageHeader {
|
|||||||
/// Payload header containing information about the message contents
|
/// Payload header containing information about the message contents
|
||||||
///
|
///
|
||||||
/// 16-byte structure following the message header
|
/// 16-byte structure following the message header
|
||||||
#[derive(Debug, Default, Clone, PartialEq)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||||
pub struct PayloadHeader {
|
pub struct PayloadHeader {
|
||||||
/// Flags controlling message processing
|
/// Flags controlling message processing
|
||||||
flags: u32,
|
flags: u32,
|
||||||
@@ -104,7 +104,7 @@ pub struct PayloadHeader {
|
|||||||
/// Header for auxiliary data section
|
/// Header for auxiliary data section
|
||||||
///
|
///
|
||||||
/// 16-byte structure preceding auxiliary data
|
/// 16-byte structure preceding auxiliary data
|
||||||
#[derive(Debug, Default, PartialEq)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||||
pub struct AuxHeader {
|
pub struct AuxHeader {
|
||||||
/// Buffer size hint (often 496)
|
/// Buffer size hint (often 496)
|
||||||
buffer_size: u32,
|
buffer_size: u32,
|
||||||
@@ -141,7 +141,7 @@ pub enum AuxValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Complete protocol message
|
/// Complete protocol message
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
/// Message metadata header
|
/// Message metadata header
|
||||||
pub message_header: MessageHeader,
|
pub message_header: MessageHeader,
|
||||||
@@ -525,3 +525,14 @@ impl std::fmt::Debug for AuxValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Message {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Message")
|
||||||
|
.field("message_header", &self.message_header)
|
||||||
|
.field("payload_header", &self.payload_header)
|
||||||
|
.field("aux", &self.aux)
|
||||||
|
.field("data", &self.data.as_ref().map(pretty_print_plist))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -39,6 +39,18 @@ impl crate::IdeviceService for remote_server::RemoteServerClient<Box<dyn ReadWri
|
|||||||
async fn connect(provider: &dyn IdeviceProvider) -> Result<Self, IdeviceError> {
|
async fn connect(provider: &dyn IdeviceProvider) -> Result<Self, IdeviceError> {
|
||||||
// Establish Lockdown session
|
// Establish Lockdown session
|
||||||
let mut lockdown = LockdownClient::connect(provider).await?;
|
let mut lockdown = LockdownClient::connect(provider).await?;
|
||||||
|
|
||||||
|
let legacy = lockdown
|
||||||
|
.get_value(Some("ProductVersion"), None)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|x| x.as_string())
|
||||||
|
.and_then(|x| x.split(".").next())
|
||||||
|
.and_then(|x| x.parse::<u8>().ok())
|
||||||
|
.map(|x| x < 5)
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
lockdown
|
lockdown
|
||||||
.start_session(&provider.get_pairing_file().await?)
|
.start_session(&provider.get_pairing_file().await?)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -56,7 +68,7 @@ impl crate::IdeviceService for remote_server::RemoteServerClient<Box<dyn ReadWri
|
|||||||
let mut idevice = provider.connect(port).await?;
|
let mut idevice = provider.connect(port).await?;
|
||||||
if ssl {
|
if ssl {
|
||||||
idevice
|
idevice
|
||||||
.start_session(&provider.get_pairing_file().await?)
|
.start_session(&provider.get_pairing_file().await?, legacy)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
// Convert to transport and build client
|
// Convert to transport and build client
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ use crate::{
|
|||||||
},
|
},
|
||||||
obf,
|
obf,
|
||||||
};
|
};
|
||||||
use log::warn;
|
|
||||||
use plist::Value;
|
use plist::Value;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NotificationInfo {
|
pub struct NotificationInfo {
|
||||||
@@ -23,6 +23,7 @@ pub struct NotificationInfo {
|
|||||||
state_description: String,
|
state_description: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct NotificationsClient<'a, R: ReadWrite> {
|
pub struct NotificationsClient<'a, R: ReadWrite> {
|
||||||
/// The underlying channel used for communication
|
/// The underlying channel used for communication
|
||||||
pub channel: Channel<'a, R>,
|
pub channel: Channel<'a, R>,
|
||||||
|
|||||||
@@ -34,8 +34,8 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use log::warn;
|
|
||||||
use plist::{Dictionary, Value};
|
use plist::{Dictionary, Value};
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{IdeviceError, ReadWrite, dvt::message::AuxValue, obf};
|
use crate::{IdeviceError, ReadWrite, dvt::message::AuxValue, obf};
|
||||||
|
|
||||||
@@ -45,6 +45,7 @@ use super::remote_server::{Channel, RemoteServerClient};
|
|||||||
///
|
///
|
||||||
/// Provides methods for launching, killing, and managing processes through the
|
/// Provides methods for launching, killing, and managing processes through the
|
||||||
/// instruments protocol. Each instance maintains its own communication channel.
|
/// instruments protocol. Each instance maintains its own communication channel.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ProcessControlClient<'a, R: ReadWrite> {
|
pub struct ProcessControlClient<'a, R: ReadWrite> {
|
||||||
/// The underlying channel for communication
|
/// The underlying channel for communication
|
||||||
channel: Channel<'a, R>,
|
channel: Channel<'a, R>,
|
||||||
|
|||||||
@@ -51,8 +51,8 @@
|
|||||||
|
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
use log::{debug, warn};
|
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
IdeviceError, ReadWrite,
|
IdeviceError, ReadWrite,
|
||||||
@@ -66,6 +66,7 @@ pub const INSTRUMENTS_MESSAGE_TYPE: u32 = 2;
|
|||||||
///
|
///
|
||||||
/// Manages multiple communication channels and handles message serialization/deserialization.
|
/// Manages multiple communication channels and handles message serialization/deserialization.
|
||||||
/// Each channel operates independently and maintains its own message queue.
|
/// Each channel operates independently and maintains its own message queue.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct RemoteServerClient<R: ReadWrite> {
|
pub struct RemoteServerClient<R: ReadWrite> {
|
||||||
/// The underlying device connection
|
/// The underlying device connection
|
||||||
idevice: R,
|
idevice: R,
|
||||||
@@ -80,6 +81,7 @@ pub struct RemoteServerClient<R: ReadWrite> {
|
|||||||
/// Handle to a specific communication channel
|
/// Handle to a specific communication channel
|
||||||
///
|
///
|
||||||
/// Provides channel-specific operations for use on the remote server client.
|
/// Provides channel-specific operations for use on the remote server client.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Channel<'a, R: ReadWrite> {
|
pub struct Channel<'a, R: ReadWrite> {
|
||||||
/// Reference to parent client
|
/// Reference to parent client
|
||||||
client: &'a mut RemoteServerClient<R>,
|
client: &'a mut RemoteServerClient<R>,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use crate::{
|
|||||||
///
|
///
|
||||||
/// Provides methods for take screnn_shot through the
|
/// Provides methods for take screnn_shot through the
|
||||||
/// instruments protocol. Each instance maintains its own communication channel.
|
/// instruments protocol. Each instance maintains its own communication channel.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ScreenshotClient<'a, R: ReadWrite> {
|
pub struct ScreenshotClient<'a, R: ReadWrite> {
|
||||||
/// The underlying channel for communication
|
/// The underlying channel for communication
|
||||||
channel: Channel<'a, R>,
|
channel: Channel<'a, R>,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use crate::{Idevice, IdeviceError, IdeviceService, obf};
|
|||||||
/// Note that a running heartbeat client is required to access other services on the device.
|
/// Note that a running heartbeat client is required to access other services on the device.
|
||||||
/// Implements the standard "Marco-Polo" protocol
|
/// Implements the standard "Marco-Polo" protocol
|
||||||
/// where the host sends "Polo" in response to the device's "Marco".
|
/// where the host sends "Polo" in response to the device's "Marco".
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct HeartbeatClient {
|
pub struct HeartbeatClient {
|
||||||
/// The underlying device connection with established heartbeat service
|
/// The underlying device connection with established heartbeat service
|
||||||
pub idevice: Idevice,
|
pub idevice: Idevice,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use super::afc::AfcClient;
|
|||||||
///
|
///
|
||||||
/// HouseArrest is used to expose the container or Documents directory of an app to a host machine
|
/// HouseArrest is used to expose the container or Documents directory of an app to a host machine
|
||||||
/// over AFC (Apple File Conduit).
|
/// over AFC (Apple File Conduit).
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct HouseArrestClient {
|
pub struct HouseArrestClient {
|
||||||
/// The underlying device connection with the HouseArrest service
|
/// The underlying device connection with the HouseArrest service
|
||||||
pub idevice: Idevice,
|
pub idevice: Idevice,
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use log::warn;
|
|
||||||
use plist::Dictionary;
|
use plist::Dictionary;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{Idevice, IdeviceError, IdeviceService, obf};
|
use crate::{Idevice, IdeviceError, IdeviceService, obf};
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ use crate::{Idevice, IdeviceError, IdeviceService, obf};
|
|||||||
///
|
///
|
||||||
/// This service provides access to information about installed applications
|
/// This service provides access to information about installed applications
|
||||||
/// and can perform application management operations.
|
/// and can perform application management operations.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct InstallationProxyClient {
|
pub struct InstallationProxyClient {
|
||||||
/// The underlying device connection with established installation_proxy service
|
/// The underlying device connection with established installation_proxy service
|
||||||
pub idevice: Idevice,
|
pub idevice: Idevice,
|
||||||
|
|||||||
106
idevice/src/services/installcoordination_proxy.rs
Normal file
106
idevice/src/services/installcoordination_proxy.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
use crate::{IdeviceError, ReadWrite, RemoteXpcClient, RsdService, obf};
|
||||||
|
|
||||||
|
impl RsdService for InstallcoordinationProxy<Box<dyn ReadWrite>> {
|
||||||
|
fn rsd_service_name() -> std::borrow::Cow<'static, str> {
|
||||||
|
obf!("com.apple.remote.installcoordination_proxy")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn from_stream(stream: Box<dyn ReadWrite>) -> Result<Self, IdeviceError> {
|
||||||
|
let mut client = RemoteXpcClient::new(stream).await?;
|
||||||
|
client.do_handshake().await?;
|
||||||
|
Ok(Self { inner: client })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InstallcoordinationProxy<R: ReadWrite> {
|
||||||
|
inner: RemoteXpcClient<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: ReadWrite> InstallcoordinationProxy<R> {
|
||||||
|
// TODO: implement 2 missing functions
|
||||||
|
//
|
||||||
|
// # REVERT STASH
|
||||||
|
// Revert Stashed App (RequestType: 2)
|
||||||
|
// This request rolls back an application to a previously "stashed" version, which is typically done after a failed update.
|
||||||
|
//
|
||||||
|
// Handler: _handleRevertStashMessage_forRemoteConnection_
|
||||||
|
//
|
||||||
|
// RequestType: 2
|
||||||
|
// ProtocolVersion: 1
|
||||||
|
// BundleID: The bundle identifier of the app to revert.
|
||||||
|
//
|
||||||
|
// Action: The service creates an IXSRemoteReverter object, which calls the IXAppInstallCoordinator to perform the revert. It responds with a success or failure message.
|
||||||
|
//
|
||||||
|
// # INSTALL
|
||||||
|
// This is the most complex request. It tells the service to install a new application.
|
||||||
|
// Purpose: To stream an application binary from a client and install it on the device.
|
||||||
|
//
|
||||||
|
// Handler: _handleInstallBeginMessage_forRemoteConnection_
|
||||||
|
// RequestType: 1
|
||||||
|
// ProtocolVersion: 1
|
||||||
|
// AssetSize: The total size of the app binary.
|
||||||
|
// AssetStreamFD: A file descriptor from which the service will read the app binary.
|
||||||
|
// RemoteInstallOptions: A dictionary containing all the app's metadata, like:
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// BundleID: (String) The application's bundle identifier (e.g., com.example.myapp).
|
||||||
|
// LocalizedName: (String) The display name of the app.
|
||||||
|
// InstallMode: (uint64) An enum specifying the installation mode (e.g., full install, update).
|
||||||
|
// Importance: (uint64) An enum defining the install's priority. 1 for "user" and 2 for "system".
|
||||||
|
// InstallableType: (uint64) Specifies the type of content being installed (e.g., app, system component).
|
||||||
|
// StoreMetadata: (Dictionary) A dictionary containing App Store metadata.
|
||||||
|
// SINFData: (Data) The legacy iTunes Sinf data for DRM.
|
||||||
|
// ProvisioningProfiles: (Array of Data) An array of provisioning profiles to install alongside the app.
|
||||||
|
// IconData: (Data, Optional) The raw data for the application's icon.
|
||||||
|
// IconDataType: (uint64, Optional) An enum specifying the format of the IconData
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Action: The service creates an IXSRemoteInstaller object. It reads the app data from the file descriptor and passes it to the system's IXAppInstallCoordinator to handle the installation, placeholder creation, and data management. It sends back progress updates and a final completion message.
|
||||||
|
|
||||||
|
pub async fn uninstall_app(&mut self, bundle_id: &str) -> Result<(), IdeviceError> {
|
||||||
|
let req = crate::xpc!({
|
||||||
|
"RequestVersion": 1u64,
|
||||||
|
"ProtocolVersion": 1u64,
|
||||||
|
"RequestType": 3u64,
|
||||||
|
"BundleID": bundle_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.inner.send_object(req, true).await?;
|
||||||
|
let res = self.inner.recv_root().await?; // it responds on the root??
|
||||||
|
|
||||||
|
match res
|
||||||
|
.as_dictionary()
|
||||||
|
.and_then(|x| x.get("Success"))
|
||||||
|
.and_then(|x| x.as_boolean())
|
||||||
|
{
|
||||||
|
Some(true) => Ok(()),
|
||||||
|
_ => Err(IdeviceError::UnexpectedResponse),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn query_app_path(&mut self, bundle_id: &str) -> Result<String, IdeviceError> {
|
||||||
|
let req = crate::xpc!({
|
||||||
|
"RequestVersion": 1u64,
|
||||||
|
"ProtocolVersion": 1u64,
|
||||||
|
"RequestType": 4u64,
|
||||||
|
"BundleID": bundle_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.inner.send_object(req, true).await?;
|
||||||
|
let res = self.inner.recv_root().await?; // it responds on the root??
|
||||||
|
|
||||||
|
match res
|
||||||
|
.as_dictionary()
|
||||||
|
.and_then(|x| x.get("InstallPath"))
|
||||||
|
.and_then(|x| x.as_dictionary())
|
||||||
|
.and_then(|x| x.get("com.apple.CFURL.string"))
|
||||||
|
.and_then(|x| x.as_string())
|
||||||
|
{
|
||||||
|
Some(s) => Ok(s.to_string()),
|
||||||
|
None => Err(IdeviceError::UnexpectedResponse),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,8 @@
|
|||||||
//! Provides functionality for interacting with the lockdown service on iOS devices,
|
//! Provides functionality for interacting with the lockdown service on iOS devices,
|
||||||
//! which is the primary service for device management and service discovery.
|
//! which is the primary service for device management and service discovery.
|
||||||
|
|
||||||
use log::error;
|
|
||||||
use plist::Value;
|
use plist::Value;
|
||||||
use serde::{Deserialize, Serialize};
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{Idevice, IdeviceError, IdeviceService, obf, pairing_file};
|
use crate::{Idevice, IdeviceError, IdeviceService, obf, pairing_file};
|
||||||
|
|
||||||
@@ -15,6 +14,7 @@ use crate::{Idevice, IdeviceError, IdeviceService, obf, pairing_file};
|
|||||||
/// - Access to device information and settings
|
/// - Access to device information and settings
|
||||||
/// - Service discovery and port allocation
|
/// - Service discovery and port allocation
|
||||||
/// - Session management and security
|
/// - Session management and security
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct LockdownClient {
|
pub struct LockdownClient {
|
||||||
/// The underlying device connection with established lockdown service
|
/// The underlying device connection with established lockdown service
|
||||||
pub idevice: crate::Idevice,
|
pub idevice: crate::Idevice,
|
||||||
@@ -48,15 +48,6 @@ impl IdeviceService for LockdownClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal structure for lockdown protocol requests
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "PascalCase")]
|
|
||||||
struct LockdownRequest {
|
|
||||||
label: String,
|
|
||||||
key: Option<String>,
|
|
||||||
request: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LockdownClient {
|
impl LockdownClient {
|
||||||
/// The default TCP port for the lockdown service
|
/// The default TCP port for the lockdown service
|
||||||
pub const LOCKDOWND_PORT: u16 = 62078;
|
pub const LOCKDOWND_PORT: u16 = 62078;
|
||||||
@@ -166,6 +157,17 @@ impl LockdownClient {
|
|||||||
return Err(IdeviceError::NoEstablishedConnection);
|
return Err(IdeviceError::NoEstablishedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let legacy = self
|
||||||
|
.get_value(Some("ProductVersion"), None)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|x| x.as_string())
|
||||||
|
.and_then(|x| x.split(".").next())
|
||||||
|
.and_then(|x| x.parse::<u8>().ok())
|
||||||
|
.map(|x| x < 5)
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
let request = crate::plist!({
|
let request = crate::plist!({
|
||||||
"Label": self.idevice.label.clone(),
|
"Label": self.idevice.label.clone(),
|
||||||
"Request": "StartSession",
|
"Request": "StartSession",
|
||||||
@@ -187,7 +189,7 @@ impl LockdownClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.idevice.start_session(pairing_file).await?;
|
self.idevice.start_session(pairing_file, legacy).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,11 +255,12 @@ impl LockdownClient {
|
|||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns `IdeviceError`
|
/// Returns `IdeviceError`
|
||||||
#[cfg(feature = "pair")]
|
#[cfg(all(feature = "pair", feature = "rustls"))]
|
||||||
pub async fn pair(
|
pub async fn pair(
|
||||||
&mut self,
|
&mut self,
|
||||||
host_id: impl Into<String>,
|
host_id: impl Into<String>,
|
||||||
system_buid: impl Into<String>,
|
system_buid: impl Into<String>,
|
||||||
|
host_name: Option<&str>,
|
||||||
) -> Result<crate::pairing_file::PairingFile, IdeviceError> {
|
) -> Result<crate::pairing_file::PairingFile, IdeviceError> {
|
||||||
let host_id = host_id.into();
|
let host_id = host_id.into();
|
||||||
let system_buid = system_buid.into();
|
let system_buid = system_buid.into();
|
||||||
@@ -266,7 +269,7 @@ impl LockdownClient {
|
|||||||
let pub_key = match pub_key.as_data().map(|x| x.to_vec()) {
|
let pub_key = match pub_key.as_data().map(|x| x.to_vec()) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => {
|
None => {
|
||||||
log::warn!("Did not get public key data response");
|
tracing::warn!("Did not get public key data response");
|
||||||
return Err(IdeviceError::UnexpectedResponse);
|
return Err(IdeviceError::UnexpectedResponse);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -275,7 +278,7 @@ impl LockdownClient {
|
|||||||
let wifi_mac = match wifi_mac.as_string() {
|
let wifi_mac = match wifi_mac.as_string() {
|
||||||
Some(w) => w,
|
Some(w) => w,
|
||||||
None => {
|
None => {
|
||||||
log::warn!("Did not get WiFiAddress string");
|
tracing::warn!("Did not get WiFiAddress string");
|
||||||
return Err(IdeviceError::UnexpectedResponse);
|
return Err(IdeviceError::UnexpectedResponse);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -295,6 +298,7 @@ impl LockdownClient {
|
|||||||
let req = crate::plist!({
|
let req = crate::plist!({
|
||||||
"Label": self.idevice.label.clone(),
|
"Label": self.idevice.label.clone(),
|
||||||
"Request": "Pair",
|
"Request": "Pair",
|
||||||
|
"HostName":? host_name,
|
||||||
"PairRecord": pair_record.clone(),
|
"PairRecord": pair_record.clone(),
|
||||||
"ProtocolVersion": "2",
|
"ProtocolVersion": "2",
|
||||||
"PairingOptions": {
|
"PairingOptions": {
|
||||||
@@ -324,6 +328,23 @@ impl LockdownClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tell the device to enter recovery mode
|
||||||
|
pub async fn enter_recovery(&mut self) -> Result<(), IdeviceError> {
|
||||||
|
self.idevice
|
||||||
|
.send_plist(crate::plist!({
|
||||||
|
"Request": "EnterRecovery"
|
||||||
|
}))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let res = self.idevice.read_plist().await?;
|
||||||
|
|
||||||
|
if res.get("Request").and_then(|x| x.as_string()) == Some("EnterRecovery") {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(IdeviceError::UnexpectedResponse)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Idevice> for LockdownClient {
|
impl From<Idevice> for LockdownClient {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! Provides functionality for interacting with the misagent service on iOS devices,
|
//! Provides functionality for interacting with the misagent service on iOS devices,
|
||||||
//! which manages provisioning profiles and certificates.
|
//! which manages provisioning profiles and certificates.
|
||||||
|
|
||||||
use log::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{Idevice, IdeviceError, IdeviceService, RsdService, obf};
|
use crate::{Idevice, IdeviceError, IdeviceService, RsdService, obf};
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ use crate::{Idevice, IdeviceError, IdeviceService, RsdService, obf};
|
|||||||
/// - Installation of provisioning profiles
|
/// - Installation of provisioning profiles
|
||||||
/// - Removal of provisioning profiles
|
/// - Removal of provisioning profiles
|
||||||
/// - Querying installed profiles
|
/// - Querying installed profiles
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct MisagentClient {
|
pub struct MisagentClient {
|
||||||
/// The underlying device connection with established misagent service
|
/// The underlying device connection with established misagent service
|
||||||
pub idevice: Idevice,
|
pub idevice: Idevice,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user