mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 14:36:16 +01:00
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -112,17 +112,6 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-recursion"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "4.7.1"
|
||||
@@ -1186,7 +1175,6 @@ dependencies = [
|
||||
name = "idevice"
|
||||
version = "0.1.33"
|
||||
dependencies = [
|
||||
"async-recursion",
|
||||
"base64",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
|
||||
@@ -5,7 +5,7 @@ edition = "2024"
|
||||
|
||||
|
||||
[dependencies]
|
||||
idevice = { path = "../idevice", features = ["full"] }
|
||||
idevice = { path = "../idevice" }
|
||||
log = "0.4.26"
|
||||
simplelog = "0.12.2"
|
||||
once_cell = "1.21.1"
|
||||
@@ -14,6 +14,55 @@ libc = "0.2.171"
|
||||
plist = "1.7.1"
|
||||
plist_plus = { version = "0.2.6", features = ["dynamic"] }
|
||||
|
||||
[features]
|
||||
afc = ["idevice/afc"]
|
||||
amfi = ["idevice/amfi"]
|
||||
core_device_proxy = ["idevice/core_device_proxy"]
|
||||
crashreportcopymobile = ["idevice/crashreportcopymobile"]
|
||||
debug_proxy = ["idevice/debug_proxy"]
|
||||
dvt = ["idevice/dvt"]
|
||||
heartbeat = ["idevice/heartbeat"]
|
||||
house_arrest = ["idevice/house_arrest"]
|
||||
installation_proxy = ["idevice/installation_proxy"]
|
||||
springboardservices = ["idevice/springboardservices"]
|
||||
misagent = ["idevice/misagent"]
|
||||
mobile_image_mounter = ["idevice/mobile_image_mounter"]
|
||||
location_simulation = ["idevice/location_simulation"]
|
||||
pair = ["idevice/pair"]
|
||||
rsd = ["idevice/rsd"]
|
||||
syslog_relay = ["idevice/syslog_relay"]
|
||||
tcp = ["idevice/tcp"]
|
||||
tunnel_tcp_stack = ["idevice/tunnel_tcp_stack"]
|
||||
tss = ["idevice/tss"]
|
||||
tunneld = ["idevice/tunneld"]
|
||||
usbmuxd = ["idevice/usbmuxd"]
|
||||
xpc = ["idevice/xpc"]
|
||||
full = [
|
||||
"afc",
|
||||
"amfi",
|
||||
"core_device_proxy",
|
||||
"crashreportcopymobile",
|
||||
"debug_proxy",
|
||||
"dvt",
|
||||
"heartbeat",
|
||||
"house_arrest",
|
||||
"installation_proxy",
|
||||
"misagent",
|
||||
"mobile_image_mounter",
|
||||
"pair",
|
||||
"usbmuxd",
|
||||
"xpc",
|
||||
"location_simulation",
|
||||
"rsd",
|
||||
"tcp",
|
||||
"tunnel_tcp_stack",
|
||||
"tss",
|
||||
"tunneld",
|
||||
"springboardservices",
|
||||
"syslog_relay",
|
||||
]
|
||||
default = ["full"]
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.28.0"
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ set(EXAMPLES_DIR ${CMAKE_SOURCE_DIR}/../examples)
|
||||
|
||||
# Find all C example files
|
||||
file(GLOB EXAMPLE_SOURCES ${EXAMPLES_DIR}/*.c)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
||||
# Create an executable for each example file
|
||||
foreach(EXAMPLE_FILE ${EXAMPLE_SOURCES})
|
||||
@@ -27,14 +26,10 @@ foreach(EXAMPLE_FILE ${EXAMPLE_SOURCES})
|
||||
# Link the static Rust library
|
||||
target_link_libraries(${EXAMPLE_NAME} PRIVATE ${STATIC_LIB})
|
||||
|
||||
# Link OpenSSL
|
||||
target_link_libraries(${EXAMPLE_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto)
|
||||
|
||||
# libplist
|
||||
|
||||
if( APPLE )
|
||||
# use static linking
|
||||
pkg_search_module(PLIST REQUIRED libplist-2.0)
|
||||
find_library( LIBPLIST libplist-2.0.a REQUIRED )
|
||||
message( STATUS "(Static linking) LIBPLIST " ${LIBPLIST} )
|
||||
target_link_libraries ( ${EXAMPLE_NAME} PRIVATE ${LIBPLIST} )
|
||||
|
||||
@@ -169,7 +169,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Create TCP provider
|
||||
TcpProviderHandle *provider = NULL;
|
||||
IdeviceProviderHandle *provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file,
|
||||
"ImageMounterTest", &provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
@@ -180,13 +180,13 @@ int main(int argc, char **argv) {
|
||||
|
||||
// Connect to AFC service
|
||||
AfcClientHandle *client = NULL;
|
||||
err = afc_client_connect_tcp(provider, &client);
|
||||
err = afc_client_connect(provider, &client);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to AFC service: %d\n", err);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
return 1;
|
||||
}
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
|
||||
// Process command
|
||||
int success = 1;
|
||||
|
||||
@@ -17,7 +17,7 @@ void print_usage(const char *program_name) {
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Initialize logger
|
||||
idevice_init_logger(Debug, Disabled, NULL);
|
||||
idevice_init_logger(Info, Disabled, NULL);
|
||||
|
||||
if (argc < 2) {
|
||||
print_usage(argv[0]);
|
||||
@@ -25,7 +25,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
const char *device_ip = argv[1];
|
||||
const char *pairing_file = argc > 2 ? argv[2] : "pairing_file.plist";
|
||||
const char *pairing_file = argc > 2 ? argv[2] : "pairing.plist";
|
||||
|
||||
/*****************************************************************
|
||||
* CoreDeviceProxy Setup
|
||||
@@ -51,7 +51,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Create TCP provider
|
||||
TcpProviderHandle *tcp_provider = NULL;
|
||||
IdeviceProviderHandle *tcp_provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing,
|
||||
"DebugProxyShell", &tcp_provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
@@ -62,14 +62,13 @@ int main(int argc, char **argv) {
|
||||
|
||||
// Connect to CoreDeviceProxy
|
||||
CoreDeviceProxyHandle *core_device = NULL;
|
||||
err = core_device_proxy_connect_tcp(tcp_provider, &core_device);
|
||||
err = core_device_proxy_connect(tcp_provider, &core_device);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to CoreDeviceProxy: %d\n", err);
|
||||
tcp_provider_free(tcp_provider);
|
||||
idevice_pairing_file_free(pairing);
|
||||
idevice_provider_free(tcp_provider);
|
||||
return 1;
|
||||
}
|
||||
tcp_provider_free(tcp_provider);
|
||||
idevice_provider_free(tcp_provider);
|
||||
|
||||
// Get server RSD port
|
||||
uint16_t rsd_port;
|
||||
@@ -77,7 +76,6 @@ int main(int argc, char **argv) {
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to get server RSD port: %d\n", err);
|
||||
core_device_proxy_free(core_device);
|
||||
idevice_pairing_file_free(pairing);
|
||||
return 1;
|
||||
}
|
||||
printf("Server RSD Port: %d\n", rsd_port);
|
||||
@@ -92,92 +90,45 @@ int main(int argc, char **argv) {
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create TCP adapter: %d\n", err);
|
||||
core_device_proxy_free(core_device);
|
||||
idevice_pairing_file_free(pairing);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Connect to RSD port
|
||||
err = adapter_connect(adapter, rsd_port);
|
||||
AdapterStreamHandle *stream = NULL;
|
||||
err = adapter_connect(adapter, rsd_port, &stream);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to RSD port: %d\n", err);
|
||||
adapter_free(adapter);
|
||||
core_device_proxy_free(core_device);
|
||||
idevice_pairing_file_free(pairing);
|
||||
return 1;
|
||||
}
|
||||
printf("Successfully connected to RSD port\n");
|
||||
|
||||
/*****************************************************************
|
||||
* XPC Device Setup
|
||||
* RSD Handshake
|
||||
*****************************************************************/
|
||||
printf("\n=== Setting up XPC Device ===\n");
|
||||
printf("\n=== Performing RSD Handshake ===\n");
|
||||
|
||||
XPCDeviceAdapterHandle *xpc_device = NULL;
|
||||
err = xpc_device_new(adapter, &xpc_device);
|
||||
RsdHandshakeHandle *handshake = NULL;
|
||||
err = rsd_handshake_new(stream, &handshake);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create XPC device: %d\n", err);
|
||||
fprintf(stderr, "Failed to perform RSD handshake: %d\n", err);
|
||||
adapter_close(stream);
|
||||
adapter_free(adapter);
|
||||
core_device_proxy_free(core_device);
|
||||
idevice_pairing_file_free(pairing);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
* Get Debug Proxy Service
|
||||
*****************************************************************/
|
||||
printf("\n=== Getting Debug Proxy Service ===\n");
|
||||
|
||||
XPCServiceHandle *debug_service = NULL;
|
||||
err = xpc_device_get_service(
|
||||
xpc_device, "com.apple.internal.dt.remote.debugproxy", &debug_service);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to get debug proxy service: %d\n", err);
|
||||
xpc_device_free(xpc_device);
|
||||
adapter_free(adapter);
|
||||
core_device_proxy_free(core_device);
|
||||
idevice_pairing_file_free(pairing);
|
||||
return 1;
|
||||
}
|
||||
printf("Debug Proxy Service Port: %d\n", debug_service->port);
|
||||
|
||||
/*****************************************************************
|
||||
* Debug Proxy Setup
|
||||
*****************************************************************/
|
||||
printf("\n=== Setting up Debug Proxy ===\n");
|
||||
|
||||
// Get the adapter back from XPC device
|
||||
AdapterHandle *debug_adapter = NULL;
|
||||
err = xpc_device_adapter_into_inner(xpc_device, &debug_adapter);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to extract adapter: %d\n", err);
|
||||
xpc_service_free(debug_service);
|
||||
xpc_device_free(xpc_device);
|
||||
core_device_proxy_free(core_device);
|
||||
idevice_pairing_file_free(pairing);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Connect to debug proxy port
|
||||
err = adapter_connect(debug_adapter, debug_service->port);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to debug proxy port: %d\n", err);
|
||||
adapter_free(debug_adapter);
|
||||
xpc_service_free(debug_service);
|
||||
core_device_proxy_free(core_device);
|
||||
idevice_pairing_file_free(pairing);
|
||||
return 1;
|
||||
}
|
||||
printf("Successfully connected to debug proxy port\n");
|
||||
|
||||
// Create DebugProxyClient
|
||||
DebugProxyAdapterHandle *debug_proxy = NULL;
|
||||
err = debug_proxy_adapter_new(debug_adapter, &debug_proxy);
|
||||
DebugProxyHandle *debug_proxy = NULL;
|
||||
err = debug_proxy_connect_rsd(adapter, handshake, &debug_proxy);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create debug proxy client: %d\n", err);
|
||||
adapter_free(debug_adapter);
|
||||
xpc_service_free(debug_service);
|
||||
core_device_proxy_free(core_device);
|
||||
idevice_pairing_file_free(pairing);
|
||||
rsd_handshake_free(handshake);
|
||||
adapter_free(adapter);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -264,7 +215,8 @@ int main(int argc, char **argv) {
|
||||
* Cleanup
|
||||
*****************************************************************/
|
||||
debug_proxy_free(debug_proxy);
|
||||
xpc_service_free(debug_service);
|
||||
rsd_handshake_free(handshake);
|
||||
adapter_free(adapter);
|
||||
|
||||
printf("\nDebug session ended\n");
|
||||
return 0;
|
||||
|
||||
@@ -15,7 +15,7 @@ void print_usage(const char *program_name) {
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Initialize logger
|
||||
idevice_init_logger(Debug, Disabled, NULL);
|
||||
idevice_init_logger(Info, Disabled, NULL);
|
||||
|
||||
if (argc < 3) {
|
||||
print_usage(argv[0]);
|
||||
@@ -42,17 +42,17 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Read pairing file
|
||||
IdevicePairingFile *pairing = NULL;
|
||||
IdeviceErrorCode err = idevice_pairing_file_read(pairing_file, &pairing);
|
||||
struct IdevicePairingFile *pairing = NULL;
|
||||
enum IdeviceErrorCode err = idevice_pairing_file_read(pairing_file, &pairing);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to read pairing file: %d\n", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create TCP provider
|
||||
TcpProviderHandle *tcp_provider = NULL;
|
||||
struct IdeviceProviderHandle *provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing,
|
||||
"ProcessDebugTest", &tcp_provider);
|
||||
"ProcessDebugTest", &provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create TCP provider: %d\n", err);
|
||||
idevice_pairing_file_free(pairing);
|
||||
@@ -60,14 +60,14 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Connect to CoreDeviceProxy
|
||||
CoreDeviceProxyHandle *core_device = NULL;
|
||||
err = core_device_proxy_connect_tcp(tcp_provider, &core_device);
|
||||
struct CoreDeviceProxyHandle *core_device = NULL;
|
||||
err = core_device_proxy_connect(provider, &core_device);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to CoreDeviceProxy: %d\n", err);
|
||||
tcp_provider_free(tcp_provider);
|
||||
idevice_provider_free(provider);
|
||||
return 1;
|
||||
}
|
||||
tcp_provider_free(tcp_provider);
|
||||
idevice_provider_free(provider);
|
||||
|
||||
// Get server RSD port
|
||||
uint16_t rsd_port;
|
||||
@@ -84,7 +84,7 @@ int main(int argc, char **argv) {
|
||||
*****************************************************************/
|
||||
printf("\n=== Creating TCP Tunnel Adapter ===\n");
|
||||
|
||||
AdapterHandle *adapter = NULL;
|
||||
struct AdapterHandle *adapter = NULL;
|
||||
err = core_device_proxy_create_tcp_adapter(core_device, &adapter);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create TCP adapter: %d\n", err);
|
||||
@@ -93,46 +93,59 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Connect to RSD port
|
||||
err = adapter_connect(adapter, rsd_port);
|
||||
struct ReadWriteOpaque *rsd_stream = NULL;
|
||||
err = adapter_connect(adapter, rsd_port, &rsd_stream);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to RSD port: %d\n", err);
|
||||
adapter_free(adapter);
|
||||
core_device_proxy_free(core_device);
|
||||
return 1;
|
||||
}
|
||||
printf("Successfully connected to RSD port\n");
|
||||
|
||||
adapter_pcap(adapter, "jit.pcap");
|
||||
|
||||
/*****************************************************************
|
||||
* XPC Device Setup
|
||||
* RSD Handshake
|
||||
*****************************************************************/
|
||||
printf("\n=== Setting up XPC Device ===\n");
|
||||
printf("\n=== Performing RSD Handshake ===\n");
|
||||
|
||||
XPCDeviceAdapterHandle *xpc_device = NULL;
|
||||
err = xpc_device_new(adapter, &xpc_device);
|
||||
struct RsdHandshakeHandle *rsd_handshake = NULL;
|
||||
err = rsd_handshake_new(rsd_stream, &rsd_handshake);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create XPC device: %d\n", err);
|
||||
fprintf(stderr, "Failed to create RSD handshake: %d\n", err);
|
||||
adapter_free(adapter);
|
||||
core_device_proxy_free(core_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get DebugProxy service
|
||||
XPCServiceHandle *debug_service = NULL;
|
||||
err = xpc_device_get_service(
|
||||
xpc_device, "com.apple.internal.dt.remote.debugproxy", &debug_service);
|
||||
// Get services
|
||||
struct CRsdServiceArray *services = NULL;
|
||||
err = rsd_get_services(rsd_handshake, &services);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to get debug proxy service: %d\n", err);
|
||||
fprintf(stderr, "Failed to get RSD services: %d\n", err);
|
||||
rsd_handshake_free(rsd_handshake);
|
||||
adapter_free(adapter);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get ProcessControl service
|
||||
XPCServiceHandle *pc_service = NULL;
|
||||
err = xpc_device_get_service(xpc_device, "com.apple.instruments.dtservicehub",
|
||||
&pc_service);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to get process control service: %d\n", err);
|
||||
xpc_device_free(xpc_device);
|
||||
core_device_proxy_free(core_device);
|
||||
// Find debug proxy and process control services
|
||||
uint16_t debug_port = 0;
|
||||
uint16_t pc_port = 0;
|
||||
|
||||
for (size_t i = 0; i < services->count; i++) {
|
||||
struct CRsdService *service = &services->services[i];
|
||||
if (strcmp(service->name, "com.apple.internal.dt.remote.debugproxy") == 0) {
|
||||
debug_port = service->port;
|
||||
} else if (strcmp(service->name, "com.apple.instruments.dtservicehub") ==
|
||||
0) {
|
||||
pc_port = service->port;
|
||||
}
|
||||
}
|
||||
|
||||
rsd_free_services(services);
|
||||
|
||||
if (debug_port == 0 || pc_port == 0) {
|
||||
fprintf(stderr, "Required services not found\n");
|
||||
adapter_free(adapter);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -141,46 +154,31 @@ int main(int argc, char **argv) {
|
||||
*****************************************************************/
|
||||
printf("\n=== Launching App ===\n");
|
||||
|
||||
// Get the adapter back from XPC device
|
||||
AdapterHandle *pc_adapter = NULL;
|
||||
err = xpc_device_adapter_into_inner(xpc_device, &pc_adapter);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to extract adapter: %d\n", err);
|
||||
xpc_device_free(xpc_device);
|
||||
core_device_proxy_free(core_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Connect to process control port
|
||||
err = adapter_connect(pc_adapter, pc_service->port);
|
||||
struct ReadWriteOpaque *pc_stream = NULL;
|
||||
err = adapter_connect(adapter, pc_port, &pc_stream);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to process control port: %d\n", err);
|
||||
adapter_free(pc_adapter);
|
||||
xpc_service_free(pc_service);
|
||||
core_device_proxy_free(core_device);
|
||||
adapter_free(adapter);
|
||||
return 1;
|
||||
}
|
||||
printf("Successfully connected to process control port\n");
|
||||
|
||||
// Create RemoteServerClient
|
||||
RemoteServerAdapterHandle *remote_server = NULL;
|
||||
err = remote_server_adapter_new(pc_adapter, &remote_server);
|
||||
struct RemoteServerHandle *remote_server = NULL;
|
||||
err = remote_server_new(pc_stream, &remote_server);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create remote server: %d\n", err);
|
||||
adapter_free(pc_adapter);
|
||||
xpc_service_free(pc_service);
|
||||
core_device_proxy_free(core_device);
|
||||
adapter_free(adapter);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create ProcessControlClient
|
||||
ProcessControlAdapterHandle *process_control = NULL;
|
||||
struct ProcessControlHandle *process_control = NULL;
|
||||
err = process_control_new(remote_server, &process_control);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create process control client: %d\n", err);
|
||||
remote_server_free(remote_server);
|
||||
xpc_service_free(pc_service);
|
||||
core_device_proxy_free(core_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -192,8 +190,6 @@ int main(int argc, char **argv) {
|
||||
fprintf(stderr, "Failed to launch app: %d\n", err);
|
||||
process_control_free(process_control);
|
||||
remote_server_free(remote_server);
|
||||
xpc_service_free(pc_service);
|
||||
core_device_proxy_free(core_device);
|
||||
return 1;
|
||||
}
|
||||
printf("Successfully launched app with PID: %" PRIu64 "\n", pid);
|
||||
@@ -203,44 +199,24 @@ int main(int argc, char **argv) {
|
||||
*****************************************************************/
|
||||
printf("\n=== Attaching Debugger ===\n");
|
||||
|
||||
// Get the adapter back from the remote server
|
||||
AdapterHandle *debug_adapter = NULL;
|
||||
err = remote_server_adapter_into_inner(remote_server, &debug_adapter);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to extract adapter: %d\n", err);
|
||||
xpc_service_free(debug_service);
|
||||
process_control_free(process_control);
|
||||
remote_server_free(remote_server);
|
||||
xpc_service_free(pc_service);
|
||||
core_device_proxy_free(core_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Connect to debug proxy port
|
||||
err = adapter_connect(debug_adapter, debug_service->port);
|
||||
struct ReadWriteOpaque *debug_stream = NULL;
|
||||
err = adapter_connect(adapter, debug_port, &debug_stream);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to debug proxy port: %d\n", err);
|
||||
adapter_free(debug_adapter);
|
||||
xpc_service_free(debug_service);
|
||||
process_control_free(process_control);
|
||||
remote_server_free(remote_server);
|
||||
xpc_service_free(pc_service);
|
||||
core_device_proxy_free(core_device);
|
||||
return 1;
|
||||
}
|
||||
printf("Successfully connected to debug proxy port\n");
|
||||
|
||||
// Create DebugProxyClient
|
||||
DebugProxyAdapterHandle *debug_proxy = NULL;
|
||||
err = debug_proxy_adapter_new(debug_adapter, &debug_proxy);
|
||||
struct DebugProxyHandle *debug_proxy = NULL;
|
||||
err = debug_proxy_connect_rsd(adapter, rsd_handshake, &debug_proxy);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create debug proxy client: %d\n", err);
|
||||
adapter_free(debug_adapter);
|
||||
xpc_service_free(debug_service);
|
||||
process_control_free(process_control);
|
||||
remote_server_free(remote_server);
|
||||
xpc_service_free(pc_service);
|
||||
core_device_proxy_free(core_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -248,17 +224,13 @@ int main(int argc, char **argv) {
|
||||
char attach_command[64];
|
||||
snprintf(attach_command, sizeof(attach_command), "vAttach;%" PRIx64, pid);
|
||||
|
||||
DebugserverCommandHandle *attach_cmd =
|
||||
struct DebugserverCommandHandle *attach_cmd =
|
||||
debugserver_command_new(attach_command, NULL, 0);
|
||||
if (attach_cmd == NULL) {
|
||||
fprintf(stderr, "Failed to create attach command\n");
|
||||
debug_proxy_free(debug_proxy);
|
||||
adapter_free(debug_adapter);
|
||||
xpc_service_free(debug_service);
|
||||
process_control_free(process_control);
|
||||
remote_server_free(remote_server);
|
||||
xpc_service_free(pc_service);
|
||||
core_device_proxy_free(core_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -274,7 +246,8 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Send detach command
|
||||
DebugserverCommandHandle *detach_cmd = debugserver_command_new("D", NULL, 0);
|
||||
struct DebugserverCommandHandle *detach_cmd =
|
||||
debugserver_command_new("D", NULL, 0);
|
||||
if (detach_cmd == NULL) {
|
||||
fprintf(stderr, "Failed to create detach command\n");
|
||||
} else {
|
||||
@@ -296,7 +269,10 @@ int main(int argc, char **argv) {
|
||||
* Cleanup
|
||||
*****************************************************************/
|
||||
debug_proxy_free(debug_proxy);
|
||||
xpc_service_free(debug_service);
|
||||
process_control_free(process_control);
|
||||
remote_server_free(remote_server);
|
||||
adapter_free(adapter);
|
||||
rsd_handshake_free(rsd_handshake);
|
||||
|
||||
printf("\nDebug session completed\n");
|
||||
return 0;
|
||||
|
||||
@@ -28,7 +28,7 @@ int main() {
|
||||
}
|
||||
|
||||
// Create TCP provider
|
||||
TcpProviderHandle *provider = NULL;
|
||||
IdeviceProviderHandle *provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file,
|
||||
"ExampleProvider", &provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
@@ -39,13 +39,13 @@ int main() {
|
||||
|
||||
// Connect to installation proxy
|
||||
HeartbeatClientHandle *client = NULL;
|
||||
err = heartbeat_connect_tcp(provider, &client);
|
||||
err = heartbeat_connect(provider, &client);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to installation proxy: %d\n", err);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
return 1;
|
||||
}
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
|
||||
u_int64_t current_interval = 15;
|
||||
while (1) {
|
||||
|
||||
@@ -7,187 +7,189 @@
|
||||
#include <unistd.h>
|
||||
|
||||
void print_usage() {
|
||||
printf("Usage: ipa_installer [options] <ipa_path>\n");
|
||||
printf("Options:\n");
|
||||
printf(" --ip IP_ADDRESS Device IP address (default: 10.7.0.2)\n");
|
||||
printf(" --pairing FILE Pairing file path (default: pairing_file.plist)\n");
|
||||
printf(" --udid UDID Device UDID (optional)\n");
|
||||
printf("Usage: ipa_installer [options] <ipa_path>\n");
|
||||
printf("Options:\n");
|
||||
printf(" --ip IP_ADDRESS Device IP address (default: 10.7.0.2)\n");
|
||||
printf(" --pairing FILE Pairing file path (default: "
|
||||
"pairing_file.plist)\n");
|
||||
printf(" --udid UDID Device UDID (optional)\n");
|
||||
}
|
||||
|
||||
int read_file(const char *filename, uint8_t **data, size_t *length) {
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
perror("Failed to open file");
|
||||
return 0;
|
||||
}
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
perror("Failed to open file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
*length = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
*data = malloc(*length);
|
||||
if (!*data) {
|
||||
perror("Failed to allocate memory");
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fread(*data, 1, *length, file) != *length) {
|
||||
perror("Failed to read file");
|
||||
free(*data);
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
fseek(file, 0, SEEK_END);
|
||||
*length = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
*data = malloc(*length);
|
||||
if (!*data) {
|
||||
perror("Failed to allocate memory");
|
||||
fclose(file);
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fread(*data, 1, *length, file) != *length) {
|
||||
perror("Failed to read file");
|
||||
free(*data);
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Initialize logger
|
||||
idevice_init_logger(Debug, Disabled, NULL);
|
||||
// Initialize logger
|
||||
idevice_init_logger(Debug, Disabled, NULL);
|
||||
|
||||
// Default values
|
||||
char *ip = "10.7.0.2";
|
||||
char *pairing_file_path = "pairing_file.plist";
|
||||
char *udid = NULL;
|
||||
char *ipa_path = NULL;
|
||||
// Default values
|
||||
char *ip = "10.7.0.2";
|
||||
char *pairing_file_path = "pairing_file.plist";
|
||||
char *udid = NULL;
|
||||
char *ipa_path = NULL;
|
||||
|
||||
// Parse arguments
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--ip") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
printf("Error: Missing IP address argument\n");
|
||||
return 1;
|
||||
}
|
||||
ip = argv[++i];
|
||||
} else if (strcmp(argv[i], "--pairing") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
printf("Error: Missing pairing file argument\n");
|
||||
return 1;
|
||||
}
|
||||
pairing_file_path = argv[++i];
|
||||
} else if (strcmp(argv[i], "--udid") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
printf("Error: Missing UDID argument\n");
|
||||
return 1;
|
||||
}
|
||||
udid = argv[++i];
|
||||
} else if (strcmp(argv[i], "help") == 0) {
|
||||
print_usage();
|
||||
return 0;
|
||||
} else {
|
||||
ipa_path = argv[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ipa_path) {
|
||||
print_usage();
|
||||
// Parse arguments
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--ip") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
printf("Error: Missing IP address argument\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create the socket address
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(LOCKDOWN_PORT);
|
||||
if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) {
|
||||
fprintf(stderr, "Invalid IP address\n");
|
||||
}
|
||||
ip = argv[++i];
|
||||
} else if (strcmp(argv[i], "--pairing") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
printf("Error: Missing pairing file argument\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read pairing file
|
||||
IdevicePairingFile *pairing_file = NULL;
|
||||
IdeviceErrorCode err = idevice_pairing_file_read(pairing_file_path, &pairing_file);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to read pairing file: %d\n", err);
|
||||
}
|
||||
pairing_file_path = argv[++i];
|
||||
} else if (strcmp(argv[i], "--udid") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
printf("Error: Missing UDID argument\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create TCP provider
|
||||
TcpProviderHandle *provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file,
|
||||
"IPAInstaller", &provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create TCP provider: %d\n", err);
|
||||
idevice_pairing_file_free(pairing_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Connect to AFC service
|
||||
AfcClientHandle *afc_client = NULL;
|
||||
err = afc_client_connect_tcp(provider, &afc_client);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to AFC service: %d\n", err);
|
||||
tcp_provider_free(provider);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Extract filename from path
|
||||
char *filename = strrchr(ipa_path, '/');
|
||||
if (filename == NULL) {
|
||||
filename = ipa_path;
|
||||
}
|
||||
udid = argv[++i];
|
||||
} else if (strcmp(argv[i], "help") == 0) {
|
||||
print_usage();
|
||||
return 0;
|
||||
} else {
|
||||
filename++; // Skip the '/'
|
||||
ipa_path = argv[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create destination path
|
||||
char dest_path[256];
|
||||
snprintf(dest_path, sizeof(dest_path), "/PublicStaging/%s", filename);
|
||||
if (!ipa_path) {
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Upload IPA file
|
||||
printf("Uploading %s to %s...\n", ipa_path, dest_path);
|
||||
uint8_t *data = NULL;
|
||||
size_t length = 0;
|
||||
if (!read_file(ipa_path, &data, &length)) {
|
||||
fprintf(stderr, "Failed to read IPA file\n");
|
||||
afc_client_free(afc_client);
|
||||
return 1;
|
||||
}
|
||||
// Create the socket address
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(LOCKDOWN_PORT);
|
||||
if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) {
|
||||
fprintf(stderr, "Invalid IP address\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
AfcFileHandle *file = NULL;
|
||||
err = afc_file_open(afc_client, dest_path, AfcWrOnly | AfcCreat, &file);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to open destination file: %d\n", err);
|
||||
free(data);
|
||||
afc_client_free(afc_client);
|
||||
return 1;
|
||||
}
|
||||
// Read pairing file
|
||||
IdevicePairingFile *pairing_file = NULL;
|
||||
IdeviceErrorCode err =
|
||||
idevice_pairing_file_read(pairing_file_path, &pairing_file);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to read pairing file: %d\n", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = afc_file_write(file, data, length);
|
||||
free(data);
|
||||
afc_file_close(file);
|
||||
// Create TCP provider
|
||||
IdeviceProviderHandle *provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file,
|
||||
"IPAInstaller", &provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create TCP provider: %d\n", err);
|
||||
idevice_pairing_file_free(pairing_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to write file: %d\n", err);
|
||||
afc_client_free(afc_client);
|
||||
return 1;
|
||||
}
|
||||
printf("Upload completed successfully\n");
|
||||
// Connect to AFC service
|
||||
AfcClientHandle *afc_client = NULL;
|
||||
err = afc_client_connect(provider, &afc_client);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to AFC service: %d\n", err);
|
||||
idevice_provider_free(provider);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Connect to installation proxy
|
||||
InstallationProxyClientHandle *instproxy_client = NULL;
|
||||
err = installation_proxy_connect_tcp(provider, &instproxy_client);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to installation proxy: %d\n", err);
|
||||
afc_client_free(afc_client);
|
||||
return 1;
|
||||
}
|
||||
// Extract filename from path
|
||||
char *filename = strrchr(ipa_path, '/');
|
||||
if (filename == NULL) {
|
||||
filename = ipa_path;
|
||||
} else {
|
||||
filename++; // Skip the '/'
|
||||
}
|
||||
|
||||
// Install the uploaded IPA
|
||||
printf("Installing %s...\n", dest_path);
|
||||
err = installation_proxy_install(instproxy_client, dest_path, NULL);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to install IPA: %d\n", err);
|
||||
} else {
|
||||
printf("Installation completed successfully\n");
|
||||
}
|
||||
// Create destination path
|
||||
char dest_path[256];
|
||||
snprintf(dest_path, sizeof(dest_path), "/PublicStaging/%s", filename);
|
||||
|
||||
// Cleanup
|
||||
installation_proxy_client_free(instproxy_client);
|
||||
// Upload IPA file
|
||||
printf("Uploading %s to %s...\n", ipa_path, dest_path);
|
||||
uint8_t *data = NULL;
|
||||
size_t length = 0;
|
||||
if (!read_file(ipa_path, &data, &length)) {
|
||||
fprintf(stderr, "Failed to read IPA file\n");
|
||||
afc_client_free(afc_client);
|
||||
tcp_provider_free(provider);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return err == IdeviceSuccess ? 0 : 1;
|
||||
AfcFileHandle *file = NULL;
|
||||
err = afc_file_open(afc_client, dest_path, AfcWrOnly, &file);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to open destination file: %d\n", err);
|
||||
free(data);
|
||||
afc_client_free(afc_client);
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = afc_file_write(file, data, length);
|
||||
free(data);
|
||||
afc_file_close(file);
|
||||
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to write file: %d\n", err);
|
||||
afc_client_free(afc_client);
|
||||
return 1;
|
||||
}
|
||||
printf("Upload completed successfully\n");
|
||||
|
||||
// Connect to installation proxy
|
||||
InstallationProxyClientHandle *instproxy_client = NULL;
|
||||
err = installation_proxy_connect_tcp(provider, &instproxy_client);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to installation proxy: %d\n", err);
|
||||
afc_client_free(afc_client);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Install the uploaded IPA
|
||||
printf("Installing %s...\n", dest_path);
|
||||
err = installation_proxy_install(instproxy_client, dest_path, NULL);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to install IPA: %d\n", err);
|
||||
} else {
|
||||
printf("Installation completed successfully\n");
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
installation_proxy_client_free(instproxy_client);
|
||||
afc_client_free(afc_client);
|
||||
idevice_provider_free(provider);
|
||||
|
||||
return err == IdeviceSuccess ? 0 : 1;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ int main() {
|
||||
}
|
||||
|
||||
// Create TCP provider
|
||||
TcpProviderHandle *provider = NULL;
|
||||
IdeviceProviderHandle *provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file,
|
||||
"ExampleProvider", &provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
@@ -41,7 +41,7 @@ int main() {
|
||||
err = installation_proxy_connect_tcp(provider, &client);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to installation proxy: %d\n", err);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ int main() {
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to get apps: %d\n", err);
|
||||
installation_proxy_client_free(client);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ int main() {
|
||||
|
||||
// Cleanup
|
||||
installation_proxy_client_free(client);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Create TCP provider
|
||||
TcpProviderHandle *tcp_provider = NULL;
|
||||
IdeviceProviderHandle *tcp_provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing,
|
||||
"LocationSimCLI", &tcp_provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
@@ -51,8 +51,8 @@ int main(int argc, char **argv) {
|
||||
|
||||
// Connect to CoreDeviceProxy
|
||||
CoreDeviceProxyHandle *core_device = NULL;
|
||||
err = core_device_proxy_connect_tcp(tcp_provider, &core_device);
|
||||
tcp_provider_free(tcp_provider);
|
||||
err = core_device_proxy_connect(tcp_provider, &core_device);
|
||||
idevice_provider_free(tcp_provider);
|
||||
idevice_pairing_file_free(pairing);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to CoreDeviceProxy: %d\n", err);
|
||||
@@ -77,62 +77,36 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = adapter_connect(adapter, rsd_port);
|
||||
// Connect to RSD port
|
||||
AdapterStreamHandle *stream = NULL;
|
||||
err = adapter_connect(adapter, rsd_port, &stream);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to RSD port: %d\n", err);
|
||||
adapter_free(adapter);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create XPC device
|
||||
XPCDeviceAdapterHandle *xpc_device = NULL;
|
||||
err = xpc_device_new(adapter, &xpc_device);
|
||||
RsdHandshakeHandle *handshake = NULL;
|
||||
err = rsd_handshake_new(stream, &handshake);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create XPC device: %d\n", err);
|
||||
fprintf(stderr, "Failed to perform RSD handshake: %d\n", err);
|
||||
adapter_close(stream);
|
||||
adapter_free(adapter);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get debug proxy service
|
||||
XPCServiceHandle *dvt_service = NULL;
|
||||
err = xpc_device_get_service(
|
||||
xpc_device, "com.apple.instruments.server.services.LocationSimulation",
|
||||
&dvt_service);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to get DVT service: %d\n", err);
|
||||
xpc_device_free(xpc_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Reuse the adapter and connect to debug proxy port
|
||||
AdapterHandle *debug_adapter = NULL;
|
||||
err = xpc_device_adapter_into_inner(xpc_device, &debug_adapter);
|
||||
xpc_device_free(xpc_device);
|
||||
xpc_service_free(dvt_service);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to extract adapter: %d\n", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
adapter_close(debug_adapter);
|
||||
err = adapter_connect(debug_adapter, dvt_service->port);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to debug proxy port: %d\n", err);
|
||||
adapter_free(debug_adapter);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create RemoteServerClient
|
||||
RemoteServerAdapterHandle *remote_server = NULL;
|
||||
err = remote_server_adapter_new(debug_adapter, &remote_server);
|
||||
RemoteServerHandle *remote_server = NULL;
|
||||
err = remote_server_connect_rsd(adapter, handshake, &remote_server);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create remote server: %d\n", err);
|
||||
adapter_free(debug_adapter);
|
||||
adapter_free(adapter);
|
||||
rsd_handshake_free(handshake);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create LocationSimulationClient
|
||||
LocationSimulationAdapterHandle *location_sim = NULL;
|
||||
LocationSimulationHandle *location_sim = NULL;
|
||||
err = location_simulation_new(remote_server, &location_sim);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create location simulation client: %d\n", err);
|
||||
|
||||
@@ -29,7 +29,7 @@ int main() {
|
||||
}
|
||||
|
||||
// Create TCP provider
|
||||
TcpProviderHandle *provider = NULL;
|
||||
IdeviceProviderHandle *provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file,
|
||||
"LockdowndTest", &provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
@@ -40,10 +40,10 @@ int main() {
|
||||
|
||||
// Connect to lockdownd
|
||||
LockdowndClientHandle *client = NULL;
|
||||
err = lockdownd_connect_tcp(provider, &client);
|
||||
err = lockdownd_connect(provider, &client);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to lockdownd: %d\n", err);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ int main() {
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to start session: %d\n", err);
|
||||
lockdownd_client_free(client);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ int main() {
|
||||
|
||||
// Cleanup
|
||||
lockdownd_client_free(client);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Create TCP provider
|
||||
TcpProviderHandle *provider = NULL;
|
||||
IdeviceProviderHandle *provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file,
|
||||
"ImageMounterTest", &provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
@@ -124,10 +124,10 @@ int main(int argc, char **argv) {
|
||||
|
||||
// Connect to lockdownd
|
||||
LockdowndClientHandle *lockdown_client = NULL;
|
||||
err = lockdownd_connect_tcp(provider, &lockdown_client);
|
||||
err = lockdownd_connect(provider, &lockdown_client);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to lockdownd: %d\n", err);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
free(image);
|
||||
free(trustcache);
|
||||
free(build_manifest);
|
||||
@@ -139,7 +139,7 @@ int main(int argc, char **argv) {
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to start session: %d\n", err);
|
||||
lockdownd_client_free(lockdown_client);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
idevice_pairing_file_free(pairing_file_2);
|
||||
free(image);
|
||||
free(trustcache);
|
||||
@@ -155,7 +155,7 @@ int main(int argc, char **argv) {
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to get UniqueChipID: %d\n", err);
|
||||
lockdownd_client_free(lockdown_client);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
free(image);
|
||||
free(trustcache);
|
||||
free(build_manifest);
|
||||
@@ -168,11 +168,11 @@ int main(int argc, char **argv) {
|
||||
|
||||
// Connect to image mounter
|
||||
ImageMounterHandle *mounter_client = NULL;
|
||||
err = image_mounter_connect_tcp(provider, &mounter_client);
|
||||
err = image_mounter_connect(provider, &mounter_client);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to image mounter: %d\n", err);
|
||||
lockdownd_client_free(lockdown_client);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
free(image);
|
||||
free(trustcache);
|
||||
free(build_manifest);
|
||||
@@ -180,7 +180,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Mount personalized image with progress callback
|
||||
err = image_mounter_mount_personalized_tcp_with_callback(
|
||||
err = image_mounter_mount_personalized_with_callback(
|
||||
mounter_client, provider, image, image_len, trustcache, trustcache_len,
|
||||
build_manifest, manifest_len,
|
||||
NULL, // info_plist
|
||||
@@ -195,7 +195,7 @@ int main(int argc, char **argv) {
|
||||
// Cleanup
|
||||
image_mounter_free(mounter_client);
|
||||
lockdownd_client_free(lockdown_client);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
free(image);
|
||||
free(trustcache);
|
||||
free(build_manifest);
|
||||
|
||||
@@ -112,7 +112,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Create TCP provider
|
||||
TcpProviderHandle *provider = NULL;
|
||||
IdeviceProviderHandle *provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file,
|
||||
"ImageMounterTest", &provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
@@ -123,13 +123,13 @@ int main(int argc, char **argv) {
|
||||
|
||||
// Connect to image mounter
|
||||
ImageMounterHandle *client = NULL;
|
||||
err = image_mounter_connect_tcp(provider, &client);
|
||||
err = image_mounter_connect(provider, &client);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to image mounter: %d\n", err);
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
return 1;
|
||||
}
|
||||
tcp_provider_free(provider);
|
||||
idevice_provider_free(provider);
|
||||
|
||||
// Process command
|
||||
int success = 1;
|
||||
|
||||
@@ -43,7 +43,7 @@ int main(int argc, char **argv) {
|
||||
printf("=== Setting up CoreDeviceProxy ===\n");
|
||||
|
||||
// Create TCP provider
|
||||
TcpProviderHandle *tcp_provider = NULL;
|
||||
IdeviceProviderHandle *tcp_provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing,
|
||||
"ProcessControlTest", &tcp_provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
@@ -54,14 +54,14 @@ int main(int argc, char **argv) {
|
||||
|
||||
// Connect to CoreDeviceProxy
|
||||
CoreDeviceProxyHandle *core_device = NULL;
|
||||
err = core_device_proxy_connect_tcp(tcp_provider, &core_device);
|
||||
err = core_device_proxy_connect(tcp_provider, &core_device);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to CoreDeviceProxy: %d\n", err);
|
||||
tcp_provider_free(tcp_provider);
|
||||
idevice_provider_free(tcp_provider);
|
||||
idevice_pairing_file_free(pairing);
|
||||
return 1;
|
||||
}
|
||||
tcp_provider_free(tcp_provider);
|
||||
idevice_provider_free(tcp_provider);
|
||||
|
||||
// Get server RSD port
|
||||
uint16_t rsd_port;
|
||||
@@ -89,89 +89,37 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Connect to RSD port
|
||||
err = adapter_connect(adapter, rsd_port);
|
||||
AdapterStreamHandle *stream = NULL;
|
||||
err = adapter_connect(adapter, rsd_port, &stream);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to RSD port: %d\n", err);
|
||||
adapter_free(adapter);
|
||||
core_device_proxy_free(core_device);
|
||||
idevice_pairing_file_free(pairing);
|
||||
return 1;
|
||||
}
|
||||
printf("Successfully connected to RSD port\n");
|
||||
|
||||
/*****************************************************************
|
||||
* XPC Device Setup
|
||||
*****************************************************************/
|
||||
printf("\n=== Setting up XPC Device ===\n");
|
||||
|
||||
XPCDeviceAdapterHandle *xpc_device = NULL;
|
||||
err = xpc_device_new(adapter, &xpc_device);
|
||||
RsdHandshakeHandle *handshake = NULL;
|
||||
err = rsd_handshake_new(stream, &handshake);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create XPC device: %d\n", err);
|
||||
fprintf(stderr, "Failed to perform RSD handshake: %d\n", err);
|
||||
adapter_close(stream);
|
||||
adapter_free(adapter);
|
||||
core_device_proxy_free(core_device);
|
||||
idevice_pairing_file_free(pairing);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
* Get DVT Service
|
||||
*****************************************************************/
|
||||
printf("\n=== Getting Debug Proxy Service ===\n");
|
||||
|
||||
XPCServiceHandle *dvt_service = NULL;
|
||||
err = xpc_device_get_service(xpc_device, "com.apple.instruments.dtservicehub",
|
||||
&dvt_service);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to get DVT service: %d\n", err);
|
||||
xpc_device_free(xpc_device);
|
||||
return 1;
|
||||
}
|
||||
printf("Debug Proxy Service Port: %d\n", dvt_service->port);
|
||||
|
||||
/*****************************************************************
|
||||
* Remote Server Setup
|
||||
*****************************************************************/
|
||||
printf("\n=== Setting up Remote Server ===\n");
|
||||
|
||||
// Get the adapter back from XPC device
|
||||
AdapterHandle *debug_adapter = NULL;
|
||||
err = xpc_device_adapter_into_inner(xpc_device, &debug_adapter);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to extract adapter: %d\n", err);
|
||||
xpc_service_free(dvt_service);
|
||||
xpc_device_free(xpc_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Connect to debug proxy port
|
||||
adapter_close(debug_adapter);
|
||||
err = adapter_connect(debug_adapter, dvt_service->port);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to debug proxy port: %d\n", err);
|
||||
adapter_free(debug_adapter);
|
||||
xpc_service_free(dvt_service);
|
||||
return 1;
|
||||
}
|
||||
printf("Successfully connected to debug proxy port\n");
|
||||
|
||||
// Create RemoteServerClient
|
||||
RemoteServerAdapterHandle *remote_server = NULL;
|
||||
err = remote_server_adapter_new(debug_adapter, &remote_server);
|
||||
RemoteServerHandle *remote_server = NULL;
|
||||
err = remote_server_connect_rsd(adapter, handshake, &remote_server);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create remote server: %d\n", err);
|
||||
adapter_free(debug_adapter);
|
||||
xpc_service_free(dvt_service);
|
||||
adapter_free(adapter);
|
||||
rsd_handshake_free(handshake);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
* Process Control Test
|
||||
*****************************************************************/
|
||||
printf("\n=== Testing Process Control ===\n");
|
||||
|
||||
// Create ProcessControlClient
|
||||
ProcessControlAdapterHandle *process_control = NULL;
|
||||
ProcessControlHandle *process_control = NULL;
|
||||
err = process_control_new(remote_server, &process_control);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create process control client: %d\n", err);
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
// Jackson Coxson
|
||||
|
||||
#include "idevice.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void print_service_details(XPCServiceHandle *service) {
|
||||
printf(" Service Details:\n");
|
||||
printf(" Entitlement: %s\n", service->entitlement);
|
||||
printf(" Port: %d\n", service->port);
|
||||
printf(" Uses Remote XPC: %s\n",
|
||||
service->uses_remote_xpc ? "true" : "false");
|
||||
printf(" Service Version: %lld\n", service->service_version);
|
||||
|
||||
if (service->features_count > 0) {
|
||||
printf(" Features:\n");
|
||||
for (size_t i = 0; i < service->features_count; i++) {
|
||||
printf(" - %s\n", service->features[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Initialize logger
|
||||
idevice_init_logger(Debug, Disabled, NULL);
|
||||
|
||||
// Create the socket address (replace with your device's IP)
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(LOCKDOWN_PORT);
|
||||
inet_pton(AF_INET, "10.7.0.2", &addr.sin_addr);
|
||||
|
||||
// Read pairing file (replace with your pairing file path)
|
||||
IdevicePairingFile *pairing_file = NULL;
|
||||
IdeviceErrorCode err =
|
||||
idevice_pairing_file_read("pairing_file.plist", &pairing_file);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to read pairing file: %d\n", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
* TCP Provider and CoreDeviceProxy Test
|
||||
*****************************************************************/
|
||||
printf("=== Testing TCP Provider and CoreDeviceProxy ===\n");
|
||||
|
||||
// Create TCP provider
|
||||
TcpProviderHandle *tcp_provider = NULL;
|
||||
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file,
|
||||
"CoreDeviceProxyTest", &tcp_provider);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create TCP provider: %d\n", err);
|
||||
idevice_pairing_file_free(pairing_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Connect to CoreDeviceProxy
|
||||
CoreDeviceProxyHandle *core_device = NULL;
|
||||
err = core_device_proxy_connect_tcp(tcp_provider, &core_device);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to CoreDeviceProxy: %d\n", err);
|
||||
tcp_provider_free(tcp_provider);
|
||||
return 1;
|
||||
}
|
||||
tcp_provider_free(tcp_provider);
|
||||
|
||||
// Get client parameters
|
||||
uint16_t mtu;
|
||||
char *address = NULL;
|
||||
char *netmask = NULL;
|
||||
err = core_device_proxy_get_client_parameters(core_device, &mtu, &address,
|
||||
&netmask);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to get client parameters: %d\n", err);
|
||||
core_device_proxy_free(core_device);
|
||||
return 1;
|
||||
}
|
||||
printf("Client Parameters:\n");
|
||||
printf(" MTU: %d\n", mtu);
|
||||
printf(" Address: %s\n", address);
|
||||
printf(" Netmask: %s\n", netmask);
|
||||
idevice_string_free(address);
|
||||
idevice_string_free(netmask);
|
||||
|
||||
// Get server address
|
||||
char *server_address = NULL;
|
||||
err = core_device_proxy_get_server_address(core_device, &server_address);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to get server address: %d\n", err);
|
||||
core_device_proxy_free(core_device);
|
||||
return 1;
|
||||
}
|
||||
printf("Server Address: %s\n", server_address);
|
||||
idevice_string_free(server_address);
|
||||
|
||||
// Get server RSD port
|
||||
uint16_t rsd_port;
|
||||
err = core_device_proxy_get_server_rsd_port(core_device, &rsd_port);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to get server RSD port: %d\n", err);
|
||||
core_device_proxy_free(core_device);
|
||||
return 1;
|
||||
}
|
||||
printf("Server RSD Port: %d\n", rsd_port);
|
||||
|
||||
// Create TCP tunnel adapter
|
||||
AdapterHandle *adapter = NULL;
|
||||
err = core_device_proxy_create_tcp_adapter(core_device, &adapter);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create TCP adapter: %d\n", err);
|
||||
} else {
|
||||
printf("Successfully created TCP tunnel adapter\n");
|
||||
}
|
||||
err = adapter_connect(adapter, rsd_port);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to connect to RSD port: %d\n", err);
|
||||
} else {
|
||||
printf("Successfully connected to RSD port\n");
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
* XPC Device Test
|
||||
*****************************************************************/
|
||||
printf("\n=== Testing XPC Device ===\n");
|
||||
|
||||
// Create XPC device
|
||||
XPCDeviceAdapterHandle *xpc_device = NULL;
|
||||
err = xpc_device_new(adapter, &xpc_device);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to create XPC device: %d\n", err);
|
||||
core_device_proxy_free(core_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// List all services
|
||||
char **service_names = NULL;
|
||||
size_t service_count = 0;
|
||||
err =
|
||||
xpc_device_get_service_names(xpc_device, &service_names, &service_count);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to get service names: %d\n", err);
|
||||
xpc_device_free(xpc_device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Available Services (%zu):\n", service_count);
|
||||
for (size_t i = 0; i < service_count; i++) {
|
||||
printf("- %s\n", service_names[i]);
|
||||
|
||||
// Get service details for each service
|
||||
XPCServiceHandle *service = NULL;
|
||||
err = xpc_device_get_service(xpc_device, service_names[i], &service);
|
||||
if (err == IdeviceSuccess) {
|
||||
print_service_details(service);
|
||||
xpc_service_free(service);
|
||||
} else {
|
||||
printf(" Failed to get service details: %d\n", err);
|
||||
}
|
||||
}
|
||||
xpc_device_free_service_names(service_names, service_count);
|
||||
|
||||
// Test getting a specific service
|
||||
const char *test_service_name = "com.apple.internal.dt.remote.debugproxy";
|
||||
XPCServiceHandle *test_service = NULL;
|
||||
err = xpc_device_get_service(xpc_device, test_service_name, &test_service);
|
||||
if (err == IdeviceSuccess) {
|
||||
printf("\nSuccessfully retrieved service '%s':\n", test_service_name);
|
||||
print_service_details(test_service);
|
||||
xpc_service_free(test_service);
|
||||
} else {
|
||||
printf("\nFailed to get service '%s': %d\n", test_service_name, err);
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
* Adapter return
|
||||
*****************************************************************/
|
||||
AdapterHandle *adapter_return = NULL;
|
||||
err = xpc_device_adapter_into_inner(xpc_device, &adapter_return);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to extract adapter: %d\n", err);
|
||||
} else {
|
||||
printf("Successfully extracted adapter\n");
|
||||
}
|
||||
|
||||
err = adapter_close(adapter_return);
|
||||
if (err != IdeviceSuccess) {
|
||||
fprintf(stderr, "Failed to close adapter port: %d\n", err);
|
||||
} else {
|
||||
printf("Successfully closed adapter port\n");
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
* Cleanup
|
||||
*****************************************************************/
|
||||
adapter_free(adapter_return);
|
||||
|
||||
printf("\nAll tests completed successfully!\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,35 +1,49 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use std::ffi::{CString, c_char};
|
||||
use std::ffi::{CStr, c_char};
|
||||
|
||||
use idevice::tcp::stream::AdapterStream;
|
||||
|
||||
use crate::core_device_proxy::AdapterHandle;
|
||||
use crate::{IdeviceErrorCode, RUNTIME};
|
||||
use crate::{IdeviceErrorCode, RUNTIME, ReadWriteOpaque};
|
||||
|
||||
pub struct AdapterStreamHandle<'a>(pub AdapterStream<'a>);
|
||||
|
||||
/// Connects the adapter to a specific port
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - The adapter handle
|
||||
/// * [`adapter_handle`] - The adapter handle
|
||||
/// * [`port`] - The port to connect to
|
||||
/// * [`stream_handle`] - A pointer to allocate the new stream to
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # 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.
|
||||
/// Any stream allocated must be used in the same thread as the adapter. The handles are NOT thread
|
||||
/// safe.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn adapter_connect(
|
||||
handle: *mut AdapterHandle,
|
||||
adapter_handle: *mut AdapterHandle,
|
||||
port: u16,
|
||||
stream_handle: *mut *mut ReadWriteOpaque,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() {
|
||||
if adapter_handle.is_null() || stream_handle.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let adapter = unsafe { &mut (*handle).0 };
|
||||
let res = RUNTIME.block_on(async move { adapter.connect(port).await });
|
||||
let adapter = unsafe { &mut (*adapter_handle).0 };
|
||||
let res = RUNTIME.block_on(async move { AdapterStream::connect(adapter, port).await });
|
||||
|
||||
match res {
|
||||
Ok(_) => IdeviceErrorCode::IdeviceSuccess,
|
||||
Ok(r) => {
|
||||
let boxed = Box::new(ReadWriteOpaque {
|
||||
inner: Some(Box::new(r)),
|
||||
});
|
||||
unsafe { *stream_handle = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Adapter connect failed: {}", e);
|
||||
IdeviceErrorCode::AdapterIOFailed
|
||||
@@ -59,7 +73,7 @@ pub unsafe extern "C" fn adapter_pcap(
|
||||
}
|
||||
|
||||
let adapter = unsafe { &mut (*handle).0 };
|
||||
let c_str = unsafe { CString::from_raw(path as *mut c_char) };
|
||||
let c_str = unsafe { CStr::from_ptr(path) };
|
||||
let path_str = match c_str.to_str() {
|
||||
Ok(s) => s,
|
||||
Err(_) => return IdeviceErrorCode::InvalidArg,
|
||||
@@ -79,7 +93,7 @@ pub unsafe extern "C" fn adapter_pcap(
|
||||
/// Closes the adapter connection
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - The adapter handle
|
||||
/// * [`handle`] - The adapter stream handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
@@ -87,7 +101,7 @@ pub unsafe extern "C" fn adapter_pcap(
|
||||
/// # 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) -> IdeviceErrorCode {
|
||||
pub unsafe extern "C" fn adapter_close(handle: *mut AdapterStreamHandle) -> IdeviceErrorCode {
|
||||
if handle.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
@@ -119,7 +133,7 @@ pub unsafe extern "C" fn adapter_close(handle: *mut AdapterHandle) -> IdeviceErr
|
||||
/// `data` must be a valid pointer to at least `length` bytes
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn adapter_send(
|
||||
handle: *mut AdapterHandle,
|
||||
handle: *mut AdapterStreamHandle,
|
||||
data: *const u8,
|
||||
length: usize,
|
||||
) -> IdeviceErrorCode {
|
||||
@@ -158,7 +172,7 @@ pub unsafe extern "C" fn adapter_send(
|
||||
/// `length` must be a valid pointer to a usize
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn adapter_recv(
|
||||
handle: *mut AdapterHandle,
|
||||
handle: *mut AdapterStreamHandle,
|
||||
data: *mut u8,
|
||||
length: *mut usize,
|
||||
max_length: usize,
|
||||
|
||||
162
ffi/src/afc.rs
162
ffi/src/afc.rs
@@ -1,23 +1,19 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use std::ffi::c_void;
|
||||
|
||||
use idevice::{
|
||||
IdeviceError, IdeviceService,
|
||||
afc::{AfcClient, DeviceInfo, FileInfo},
|
||||
provider::IdeviceProvider,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
IdeviceErrorCode, IdeviceHandle, RUNTIME,
|
||||
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
|
||||
};
|
||||
use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle};
|
||||
|
||||
pub struct AfcClientHandle(pub AfcClient);
|
||||
|
||||
/// Connects to the AFC service using a TCP provider
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A TcpProvider
|
||||
/// * [`provider`] - An IdeviceProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated AfcClient handle
|
||||
///
|
||||
/// # Returns
|
||||
@@ -27,8 +23,8 @@ pub struct AfcClientHandle(pub AfcClient);
|
||||
/// `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 afc_client_connect_tcp(
|
||||
provider: *mut TcpProviderHandle,
|
||||
pub unsafe extern "C" fn afc_client_connect(
|
||||
provider: *mut IdeviceProviderHandle,
|
||||
client: *mut *mut AfcClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || client.is_null() {
|
||||
@@ -36,55 +32,10 @@ pub unsafe extern "C" fn afc_client_connect_tcp(
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<AfcClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
let provider_ref = &provider_box.0;
|
||||
let result = AfcClient::connect(provider_ref).await;
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
});
|
||||
let res = RUNTIME.block_on(async {
|
||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
let boxed = Box::new(AfcClientHandle(r));
|
||||
unsafe { *client = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = unsafe { Box::from_raw(provider) };
|
||||
e.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Connects to the AFC service using a Usbmuxd provider
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A UsbmuxdProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated AfcClient handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # 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 afc_client_connect_usbmuxd(
|
||||
provider: *mut UsbmuxdProviderHandle,
|
||||
client: *mut *mut AfcClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() {
|
||||
log::error!("Provider is null");
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<AfcClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
let provider_ref = &provider_box.0;
|
||||
let result = AfcClient::connect(provider_ref).await;
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
AfcClient::connect(provider_ref).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -114,7 +65,7 @@ pub unsafe extern "C" fn afc_client_new(
|
||||
socket: *mut IdeviceHandle,
|
||||
client: *mut *mut AfcClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if socket.is_null() {
|
||||
if socket.is_null() || client.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
let socket = unsafe { Box::from_raw(socket) }.0;
|
||||
@@ -231,7 +182,7 @@ pub unsafe extern "C" fn afc_make_directory(
|
||||
client: *mut AfcClientHandle,
|
||||
path: *const libc::c_char,
|
||||
) -> IdeviceErrorCode {
|
||||
if path.is_null() {
|
||||
if client.is_null() || path.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
@@ -242,11 +193,8 @@ pub unsafe extern "C" fn afc_make_directory(
|
||||
};
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.mk_dir(path).await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.mk_dir(path).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -286,7 +234,7 @@ pub unsafe extern "C" fn afc_get_file_info(
|
||||
path: *const libc::c_char,
|
||||
info: *mut AfcFileInfo,
|
||||
) -> IdeviceErrorCode {
|
||||
if path.is_null() || info.is_null() {
|
||||
if client.is_null() || path.is_null() || info.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
@@ -297,11 +245,8 @@ pub unsafe extern "C" fn afc_get_file_info(
|
||||
};
|
||||
|
||||
let res: Result<FileInfo, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.get_file_info(path).await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.get_file_info(path).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -380,16 +325,13 @@ pub unsafe extern "C" fn afc_get_device_info(
|
||||
client: *mut AfcClientHandle,
|
||||
info: *mut AfcDeviceInfo,
|
||||
) -> IdeviceErrorCode {
|
||||
if info.is_null() {
|
||||
if client.is_null() || info.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<DeviceInfo, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.get_device_info().await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.get_device_info().await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -441,7 +383,7 @@ pub unsafe extern "C" fn afc_remove_path(
|
||||
client: *mut AfcClientHandle,
|
||||
path: *const libc::c_char,
|
||||
) -> IdeviceErrorCode {
|
||||
if path.is_null() {
|
||||
if client.is_null() || path.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
@@ -452,11 +394,8 @@ pub unsafe extern "C" fn afc_remove_path(
|
||||
};
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.remove(path).await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.remove(path).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -482,7 +421,7 @@ pub unsafe extern "C" fn afc_remove_path_and_contents(
|
||||
client: *mut AfcClientHandle,
|
||||
path: *const libc::c_char,
|
||||
) -> IdeviceErrorCode {
|
||||
if path.is_null() {
|
||||
if client.is_null() || path.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
@@ -493,11 +432,8 @@ pub unsafe extern "C" fn afc_remove_path_and_contents(
|
||||
};
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.remove_all(path).await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.remove_all(path).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -531,7 +467,7 @@ impl From<AfcFopenMode> for idevice::afc::opcode::AfcFopenMode {
|
||||
|
||||
/// Handle for an open file on the device
|
||||
#[allow(dead_code)]
|
||||
pub struct AfcFileHandle(*mut c_void); // Opaque pointer
|
||||
pub struct AfcFileHandle<'a>(Box<idevice::afc::file::FileDescriptor<'a>>); // Opaque pointer
|
||||
|
||||
/// Opens a file on the device
|
||||
///
|
||||
@@ -546,7 +482,9 @@ pub struct AfcFileHandle(*mut c_void); // Opaque pointer
|
||||
///
|
||||
/// # Safety
|
||||
/// All pointers must be valid and non-null
|
||||
/// `path` must be a valid null-terminated C string
|
||||
/// `path` must be a valid null-terminated C string.
|
||||
/// The file handle MAY NOT be used from another thread, and is
|
||||
/// dependant upon the client it was created by.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn afc_file_open(
|
||||
client: *mut AfcClientHandle,
|
||||
@@ -554,7 +492,7 @@ pub unsafe extern "C" fn afc_file_open(
|
||||
mode: AfcFopenMode,
|
||||
handle: *mut *mut AfcFileHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if path.is_null() || handle.is_null() {
|
||||
if client.is_null() || path.is_null() || handle.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
@@ -567,18 +505,15 @@ pub unsafe extern "C" fn afc_file_open(
|
||||
let mode = mode.into();
|
||||
|
||||
let res: Result<*mut AfcFileHandle, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
let result = client_ref.open(path, mode).await;
|
||||
let res = match result {
|
||||
match result {
|
||||
Ok(f) => {
|
||||
let boxed = Box::new(f);
|
||||
Ok(Box::into_raw(boxed) as *mut AfcFileHandle)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
std::mem::forget(client_box);
|
||||
res
|
||||
}
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -716,7 +651,7 @@ pub unsafe extern "C" fn afc_make_link(
|
||||
source: *const libc::c_char,
|
||||
link_type: AfcLinkType,
|
||||
) -> IdeviceErrorCode {
|
||||
if target.is_null() || source.is_null() {
|
||||
if client.is_null() || target.is_null() || source.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
@@ -738,11 +673,8 @@ pub unsafe extern "C" fn afc_make_link(
|
||||
};
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.link(target, source, link_type).await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.link(target, source, link_type).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -770,7 +702,7 @@ pub unsafe extern "C" fn afc_rename_path(
|
||||
source: *const libc::c_char,
|
||||
target: *const libc::c_char,
|
||||
) -> IdeviceErrorCode {
|
||||
if source.is_null() || target.is_null() {
|
||||
if client.is_null() || source.is_null() || target.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
@@ -787,11 +719,8 @@ pub unsafe extern "C" fn afc_rename_path(
|
||||
};
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.rename(source, target).await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.rename(source, target).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -799,3 +728,18 @@ pub unsafe extern "C" fn afc_rename_path(
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees memory allocated by a file read function allocated by this library
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`info`] - Pointer to AfcDeviceInfo struct to free
|
||||
///
|
||||
/// # Safety
|
||||
/// `info` must be a valid pointer to an AfcDeviceInfo struct previously returned by afc_get_device_info
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn afc_file_read_data_free(data: *mut u8, length: libc::size_t) {
|
||||
if !data.is_null() {
|
||||
let boxed = unsafe { Box::from_raw(std::slice::from_raw_parts_mut(data, length)) };
|
||||
drop(boxed);
|
||||
}
|
||||
}
|
||||
|
||||
128
ffi/src/amfi.rs
128
ffi/src/amfi.rs
@@ -1,18 +1,15 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use idevice::{IdeviceError, IdeviceService, amfi::AmfiClient};
|
||||
use idevice::{IdeviceError, IdeviceService, amfi::AmfiClient, provider::IdeviceProvider};
|
||||
|
||||
use crate::{
|
||||
IdeviceErrorCode, IdeviceHandle, RUNTIME,
|
||||
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
|
||||
};
|
||||
use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle};
|
||||
|
||||
pub struct AmfiClientHandle(pub AmfiClient);
|
||||
|
||||
/// Automatically creates and connects to AMFI service, returning a client handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A TcpProvider
|
||||
/// * [`provider`] - An IdeviceProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated AmfiClient handle
|
||||
///
|
||||
/// # Returns
|
||||
@@ -22,8 +19,8 @@ pub struct AmfiClientHandle(pub AmfiClient);
|
||||
/// `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 amfi_connect_tcp(
|
||||
provider: *mut TcpProviderHandle,
|
||||
pub unsafe extern "C" fn amfi_connect(
|
||||
provider: *mut IdeviceProviderHandle,
|
||||
client: *mut *mut AmfiClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || client.is_null() {
|
||||
@@ -32,70 +29,10 @@ pub unsafe extern "C" fn amfi_connect_tcp(
|
||||
}
|
||||
|
||||
let res: Result<AmfiClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the provider (without immediately dropping it)
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let provider_ref = &provider_box.0;
|
||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||
|
||||
// Connect using the reference
|
||||
let result = AmfiClient::connect(provider_ref).await;
|
||||
|
||||
// Explicitly keep the provider_box alive until after connect completes
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
let boxed = Box::new(AmfiClientHandle(r));
|
||||
unsafe { *client = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => {
|
||||
// If connection failed, the provider_box was already forgotten,
|
||||
// so we need to reconstruct it to avoid leak
|
||||
let _ = unsafe { Box::from_raw(provider) };
|
||||
e.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Automatically creates and connects to AMFI service, returning a client handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A UsbmuxdProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated AmfiClient handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # 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 amfi_connect_usbmuxd(
|
||||
provider: *mut UsbmuxdProviderHandle,
|
||||
client: *mut *mut AmfiClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() {
|
||||
log::error!("Provider is null");
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<AmfiClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the provider (without immediately dropping it)
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let provider_ref = &provider_box.0;
|
||||
|
||||
// Connect using the reference
|
||||
let result = AmfiClient::connect(provider_ref).await;
|
||||
|
||||
// Explicitly keep the provider_box alive until after connect completes
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
AmfiClient::connect(provider_ref).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -118,16 +55,18 @@ pub unsafe extern "C" fn amfi_connect_usbmuxd(
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `socket` must be a valid pointer to a handle allocated by this library
|
||||
/// `socket` must be a valid pointer to a handle allocated by this library. It is consumed, and
|
||||
/// should 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 amfi_new(
|
||||
socket: *mut IdeviceHandle,
|
||||
client: *mut *mut AmfiClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if socket.is_null() {
|
||||
if socket.is_null() || client.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let socket = unsafe { Box::from_raw(socket) }.0;
|
||||
let r = AmfiClient::new(socket);
|
||||
let boxed = Box::new(AmfiClientHandle(r));
|
||||
@@ -149,16 +88,13 @@ pub unsafe extern "C" fn amfi_new(
|
||||
pub unsafe extern "C" fn amfi_reveal_developer_mode_option_in_ui(
|
||||
client: *mut AmfiClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if client.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the client
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let client_ref = &mut client_box.0;
|
||||
let res = client_ref.reveal_developer_mode_option_in_ui().await;
|
||||
|
||||
std::mem::forget(client_box);
|
||||
res
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.reveal_developer_mode_option_in_ui().await
|
||||
});
|
||||
match res {
|
||||
Ok(_) => IdeviceErrorCode::IdeviceSuccess,
|
||||
@@ -180,16 +116,13 @@ pub unsafe extern "C" fn amfi_reveal_developer_mode_option_in_ui(
|
||||
pub unsafe extern "C" fn amfi_enable_developer_mode(
|
||||
client: *mut AmfiClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if client.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the client
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let client_ref = &mut client_box.0;
|
||||
let res = client_ref.enable_developer_mode().await;
|
||||
|
||||
std::mem::forget(client_box);
|
||||
res
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.enable_developer_mode().await
|
||||
});
|
||||
match res {
|
||||
Ok(_) => IdeviceErrorCode::IdeviceSuccess,
|
||||
@@ -211,16 +144,13 @@ pub unsafe extern "C" fn amfi_enable_developer_mode(
|
||||
pub unsafe extern "C" fn amfi_accept_developer_mode(
|
||||
client: *mut AmfiClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if client.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the client
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let client_ref = &mut client_box.0;
|
||||
let res = client_ref.accept_developer_mode().await;
|
||||
|
||||
std::mem::forget(client_box);
|
||||
res
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.accept_developer_mode().await
|
||||
});
|
||||
match res {
|
||||
Ok(_) => IdeviceErrorCode::IdeviceSuccess,
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
use std::ffi::{CString, c_char};
|
||||
|
||||
use idevice::{
|
||||
IdeviceError, IdeviceService, core_device_proxy::CoreDeviceProxy, tcp::adapter::Adapter,
|
||||
IdeviceError, IdeviceService, core_device_proxy::CoreDeviceProxy, provider::IdeviceProvider,
|
||||
tcp::adapter::Adapter,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
IdeviceErrorCode, IdeviceHandle, RUNTIME,
|
||||
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
|
||||
};
|
||||
use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle};
|
||||
|
||||
pub struct CoreDeviceProxyHandle(pub CoreDeviceProxy);
|
||||
pub struct AdapterHandle(pub Adapter);
|
||||
@@ -17,7 +15,7 @@ pub struct AdapterHandle(pub Adapter);
|
||||
/// Automatically creates and connects to Core Device Proxy, returning a client handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A TcpProvider
|
||||
/// * [`provider`] - An IdeviceProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated CoreDeviceProxy handle
|
||||
///
|
||||
/// # Returns
|
||||
@@ -27,8 +25,8 @@ pub struct AdapterHandle(pub Adapter);
|
||||
/// `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 core_device_proxy_connect_tcp(
|
||||
provider: *mut TcpProviderHandle,
|
||||
pub unsafe extern "C" fn core_device_proxy_connect(
|
||||
provider: *mut IdeviceProviderHandle,
|
||||
client: *mut *mut CoreDeviceProxyHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || client.is_null() {
|
||||
@@ -37,70 +35,10 @@ pub unsafe extern "C" fn core_device_proxy_connect_tcp(
|
||||
}
|
||||
|
||||
let res: Result<CoreDeviceProxy, IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the provider (without immediately dropping it)
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let provider_ref = &provider_box.0;
|
||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||
|
||||
// Connect using the reference
|
||||
let result = CoreDeviceProxy::connect(provider_ref).await;
|
||||
|
||||
// Explicitly keep the provider_box alive until after connect completes
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
let boxed = Box::new(CoreDeviceProxyHandle(r));
|
||||
unsafe { *client = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => {
|
||||
// If connection failed, the provider_box was already forgotten,
|
||||
// so we need to reconstruct it to avoid leak
|
||||
let _ = unsafe { Box::from_raw(provider) };
|
||||
e.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Automatically creates and connects to Core Device Proxy, returning a client handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A UsbmuxdProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated CoreDeviceProxy handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # 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 core_device_proxy_connect_usbmuxd(
|
||||
provider: *mut UsbmuxdProviderHandle,
|
||||
client: *mut *mut CoreDeviceProxyHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() {
|
||||
log::error!("Provider is null");
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<CoreDeviceProxy, IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the provider (without immediately dropping it)
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let provider_ref = &provider_box.0;
|
||||
|
||||
// Connect using the reference
|
||||
let result = CoreDeviceProxy::connect(provider_ref).await;
|
||||
|
||||
// Explicitly keep the provider_box alive until after connect completes
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
CoreDeviceProxy::connect(provider_ref).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -123,14 +61,15 @@ pub unsafe extern "C" fn core_device_proxy_connect_usbmuxd(
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `socket` must be a valid pointer to a handle allocated by this library
|
||||
/// `socket` must be a valid pointer to a handle allocated by this library. It 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 core_device_proxy_new(
|
||||
socket: *mut IdeviceHandle,
|
||||
client: *mut *mut CoreDeviceProxyHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if socket.is_null() {
|
||||
if socket.is_null() || client.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
let socket = unsafe { Box::from_raw(socket) }.0;
|
||||
@@ -233,8 +172,8 @@ pub unsafe extern "C" fn core_device_proxy_recv(
|
||||
/// # Arguments
|
||||
/// * [`handle`] - The CoreDeviceProxy handle
|
||||
/// * [`mtu`] - Pointer to store the MTU value
|
||||
/// * [`address`] - Pointer to store the IP address string (must be at least 16 bytes)
|
||||
/// * [`netmask`] - Pointer to store the netmask string (must be at least 16 bytes)
|
||||
/// * [`address`] - Pointer to store the IP address string
|
||||
/// * [`netmask`] - Pointer to store the netmask string
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
@@ -262,9 +201,21 @@ pub unsafe extern "C" fn core_device_proxy_get_client_parameters(
|
||||
*mtu = params.mtu;
|
||||
}
|
||||
|
||||
// Allocate both strings, but handle partial failure
|
||||
let address_cstring = match CString::new(params.address.clone()) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return IdeviceErrorCode::InvalidString,
|
||||
};
|
||||
|
||||
let netmask_cstring = match CString::new(params.netmask.clone()) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return IdeviceErrorCode::InvalidString,
|
||||
};
|
||||
|
||||
// Only assign to output pointers after both succeed
|
||||
unsafe {
|
||||
*address = CString::new(params.address.clone()).unwrap().into_raw();
|
||||
*netmask = CString::new(params.netmask.clone()).unwrap().into_raw();
|
||||
*address = address_cstring.into_raw();
|
||||
*netmask = netmask_cstring.into_raw();
|
||||
}
|
||||
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
@@ -274,7 +225,7 @@ pub unsafe extern "C" fn core_device_proxy_get_client_parameters(
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - The CoreDeviceProxy handle
|
||||
/// * [`address`] - Pointer to store the server address string (must be at least 16 bytes)
|
||||
/// * [`address`] - Pointer to store the server address string
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
@@ -294,9 +245,10 @@ pub unsafe extern "C" fn core_device_proxy_get_server_address(
|
||||
let proxy = unsafe { &(*handle).0 };
|
||||
|
||||
unsafe {
|
||||
*address = CString::new(proxy.handshake.server_address.clone())
|
||||
.unwrap()
|
||||
.into_raw();
|
||||
*address = match CString::new(proxy.handshake.server_address.clone()) {
|
||||
Ok(s) => s.into_raw(),
|
||||
Err(_) => return IdeviceErrorCode::InvalidString,
|
||||
};
|
||||
}
|
||||
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
|
||||
@@ -5,13 +5,15 @@ use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
|
||||
use idevice::debug_proxy::{DebugProxyClient, DebugserverCommand};
|
||||
use idevice::tcp::adapter::Adapter;
|
||||
use idevice::tcp::stream::AdapterStream;
|
||||
use idevice::{IdeviceError, ReadWrite, RsdService};
|
||||
|
||||
use crate::core_device_proxy::AdapterHandle;
|
||||
use crate::rsd::RsdHandshakeHandle;
|
||||
use crate::{IdeviceErrorCode, RUNTIME};
|
||||
|
||||
/// Opaque handle to a DebugProxyClient
|
||||
pub struct DebugProxyAdapterHandle(pub DebugProxyClient<Adapter>);
|
||||
pub struct DebugProxyHandle(pub DebugProxyClient<Box<dyn ReadWrite>>);
|
||||
|
||||
/// Represents a debugserver command
|
||||
#[repr(C)]
|
||||
@@ -56,16 +58,26 @@ pub unsafe extern "C" fn debugserver_command_new(
|
||||
let argv_len = argv_vec.len();
|
||||
|
||||
let boxed = Box::new(DebugserverCommandHandle {
|
||||
name: CString::new(name).unwrap().into_raw(),
|
||||
name: match CString::new(name) {
|
||||
Ok(n) => n.into_raw(),
|
||||
Err(_) => return ptr::null_mut(),
|
||||
},
|
||||
argv: if argv_vec.is_empty() {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
let mut argv_ptrs: Vec<*mut c_char> = argv_vec
|
||||
let argv_ptrs: Result<Vec<*mut c_char>, _> = argv_vec
|
||||
.into_iter()
|
||||
.map(|s| CString::new(s).unwrap().into_raw())
|
||||
.map(|s| CString::new(s).map(|cs| cs.into_raw()))
|
||||
.collect();
|
||||
|
||||
let mut argv_ptrs = match argv_ptrs {
|
||||
Ok(ptrs) => ptrs,
|
||||
Err(_) => return ptr::null_mut(),
|
||||
};
|
||||
argv_ptrs.shrink_to_fit();
|
||||
argv_ptrs.as_mut_ptr()
|
||||
let ptr = argv_ptrs.as_mut_ptr();
|
||||
std::mem::forget(argv_ptrs);
|
||||
ptr
|
||||
},
|
||||
argv_count: argv_len,
|
||||
});
|
||||
@@ -106,7 +118,48 @@ pub unsafe extern "C" fn debugserver_command_free(command: *mut DebugserverComma
|
||||
/// Creates a new DebugProxyClient
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`socket`] - The socket to use for communication
|
||||
/// * [`provider`] - An adapter created by this library
|
||||
/// * [`handshake`] - An RSD handshake from the same provider
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `provider` must be a valid pointer to a handle allocated by this library
|
||||
/// `handshake` must be a valid pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn debug_proxy_connect_rsd(
|
||||
provider: *mut AdapterHandle,
|
||||
handshake: *mut RsdHandshakeHandle,
|
||||
handle: *mut *mut DebugProxyHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || handshake.is_null() || handshake.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
let res: Result<DebugProxyClient<AdapterStream>, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let provider_ref = unsafe { &mut (*provider).0 };
|
||||
let handshake_ref = unsafe { &mut (*handshake).0 };
|
||||
|
||||
// Connect using the reference
|
||||
DebugProxyClient::connect_rsd(provider_ref, handshake_ref).await
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(d) => {
|
||||
let boxed = Box::new(DebugProxyHandle(DebugProxyClient::new(Box::new(
|
||||
d.into_inner(),
|
||||
))));
|
||||
unsafe { *handle = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new DebugProxyClient
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`socket`] - The socket to use for communication. Any object that supports ReadWrite.
|
||||
/// * [`handle`] - Pointer to store the newly created DebugProxyClient handle
|
||||
///
|
||||
/// # Returns
|
||||
@@ -116,19 +169,19 @@ pub unsafe extern "C" fn debugserver_command_free(command: *mut DebugserverComma
|
||||
/// `socket` must be a valid pointer to a handle allocated by this library
|
||||
/// `handle` must be a valid pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn debug_proxy_adapter_new(
|
||||
socket: *mut AdapterHandle,
|
||||
handle: *mut *mut DebugProxyAdapterHandle,
|
||||
pub unsafe extern "C" fn debug_proxy_new(
|
||||
socket: *mut Box<dyn ReadWrite>,
|
||||
handle: *mut *mut DebugProxyHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if socket.is_null() || handle.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let socket = unsafe { Box::from_raw(socket) };
|
||||
let client = DebugProxyClient::new(socket.0);
|
||||
let client = DebugProxyClient::new(*socket);
|
||||
let new_handle = DebugProxyHandle(client);
|
||||
|
||||
let boxed = Box::new(DebugProxyAdapterHandle(client));
|
||||
unsafe { *handle = Box::into_raw(boxed) };
|
||||
unsafe { *handle = Box::into_raw(Box::new(new_handle)) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
@@ -140,7 +193,7 @@ pub unsafe extern "C" fn debug_proxy_adapter_new(
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library or NULL
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn debug_proxy_free(handle: *mut DebugProxyAdapterHandle) {
|
||||
pub unsafe extern "C" fn debug_proxy_free(handle: *mut DebugProxyHandle) {
|
||||
if !handle.is_null() {
|
||||
let _ = unsafe { Box::from_raw(handle) };
|
||||
}
|
||||
@@ -161,7 +214,7 @@ pub unsafe extern "C" fn debug_proxy_free(handle: *mut DebugProxyAdapterHandle)
|
||||
/// `response` must be a valid pointer to a location where the string will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn debug_proxy_send_command(
|
||||
handle: *mut DebugProxyAdapterHandle,
|
||||
handle: *mut DebugProxyHandle,
|
||||
command: *mut DebugserverCommandHandle,
|
||||
response: *mut *mut c_char,
|
||||
) -> IdeviceErrorCode {
|
||||
@@ -192,7 +245,10 @@ pub unsafe extern "C" fn debug_proxy_send_command(
|
||||
|
||||
match res {
|
||||
Ok(Some(r)) => {
|
||||
let cstr = CString::new(r).unwrap();
|
||||
let cstr = match CString::new(r) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return IdeviceErrorCode::InvalidString,
|
||||
};
|
||||
unsafe { *response = cstr.into_raw() };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
@@ -218,7 +274,7 @@ pub unsafe extern "C" fn debug_proxy_send_command(
|
||||
/// `response` must be a valid pointer to a location where the string will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn debug_proxy_read_response(
|
||||
handle: *mut DebugProxyAdapterHandle,
|
||||
handle: *mut DebugProxyHandle,
|
||||
response: *mut *mut c_char,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() || response.is_null() {
|
||||
@@ -230,7 +286,10 @@ pub unsafe extern "C" fn debug_proxy_read_response(
|
||||
|
||||
match res {
|
||||
Ok(Some(r)) => {
|
||||
let cstr = CString::new(r).unwrap();
|
||||
let cstr = match CString::new(r) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return IdeviceErrorCode::InvalidString,
|
||||
};
|
||||
unsafe { *response = cstr.into_raw() };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
@@ -257,7 +316,7 @@ pub unsafe extern "C" fn debug_proxy_read_response(
|
||||
/// `data` must be a valid pointer to `len` bytes
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn debug_proxy_send_raw(
|
||||
handle: *mut DebugProxyAdapterHandle,
|
||||
handle: *mut DebugProxyHandle,
|
||||
data: *const u8,
|
||||
len: usize,
|
||||
) -> IdeviceErrorCode {
|
||||
@@ -290,7 +349,7 @@ pub unsafe extern "C" fn debug_proxy_send_raw(
|
||||
/// `response` must be a valid pointer to a location where the string will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn debug_proxy_read(
|
||||
handle: *mut DebugProxyAdapterHandle,
|
||||
handle: *mut DebugProxyHandle,
|
||||
len: usize,
|
||||
response: *mut *mut c_char,
|
||||
) -> IdeviceErrorCode {
|
||||
@@ -303,7 +362,10 @@ pub unsafe extern "C" fn debug_proxy_read(
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
let cstr = CString::new(r).unwrap();
|
||||
let cstr = match CString::new(r) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return IdeviceErrorCode::InvalidString,
|
||||
};
|
||||
unsafe { *response = cstr.into_raw() };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
@@ -328,7 +390,7 @@ pub unsafe extern "C" fn debug_proxy_read(
|
||||
/// `response` must be a valid pointer to a location where the string will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn debug_proxy_set_argv(
|
||||
handle: *mut DebugProxyAdapterHandle,
|
||||
handle: *mut DebugProxyHandle,
|
||||
argv: *const *const c_char,
|
||||
argv_count: usize,
|
||||
response: *mut *mut c_char,
|
||||
@@ -358,7 +420,10 @@ pub unsafe extern "C" fn debug_proxy_set_argv(
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
let cstr = CString::new(r).unwrap();
|
||||
let cstr = match CString::new(r) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return IdeviceErrorCode::InvalidString,
|
||||
};
|
||||
unsafe { *response = cstr.into_raw() };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
@@ -377,9 +442,7 @@ pub unsafe extern "C" fn debug_proxy_set_argv(
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn debug_proxy_send_ack(
|
||||
handle: *mut DebugProxyAdapterHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
pub unsafe extern "C" fn debug_proxy_send_ack(handle: *mut DebugProxyHandle) -> IdeviceErrorCode {
|
||||
if handle.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
@@ -404,9 +467,7 @@ pub unsafe extern "C" fn debug_proxy_send_ack(
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn debug_proxy_send_nack(
|
||||
handle: *mut DebugProxyAdapterHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
pub unsafe extern "C" fn debug_proxy_send_nack(handle: *mut DebugProxyHandle) -> IdeviceErrorCode {
|
||||
if handle.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
@@ -429,39 +490,9 @@ pub unsafe extern "C" fn debug_proxy_send_nack(
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn debug_proxy_set_ack_mode(
|
||||
handle: *mut DebugProxyAdapterHandle,
|
||||
enabled: c_int,
|
||||
) {
|
||||
pub unsafe extern "C" fn debug_proxy_set_ack_mode(handle: *mut DebugProxyHandle, enabled: c_int) {
|
||||
if !handle.is_null() {
|
||||
let client = unsafe { &mut (*handle).0 };
|
||||
client.set_ack_mode(enabled != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying socket from a DebugProxyClient
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - The handle to get the socket from
|
||||
/// * [`adapter`] - The newly allocated ConnectionHandle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library or NULL, and never used again
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn debug_proxy_adapter_into_inner(
|
||||
handle: *mut DebugProxyAdapterHandle,
|
||||
adapter: *mut *mut AdapterHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let client = unsafe { Box::from_raw(handle) };
|
||||
let socket_obj = client.0.into_inner();
|
||||
let boxed = Box::new(AdapterHandle(socket_obj));
|
||||
unsafe { *adapter = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ pub enum IdeviceErrorCode {
|
||||
ImageNotMounted = -25,
|
||||
Reqwest = -26,
|
||||
InternalError = -27,
|
||||
Xpc = -28,
|
||||
NsKeyedArchiveError = -29,
|
||||
UnknownAuxValueType = -30,
|
||||
UnknownChannel = -31,
|
||||
@@ -50,9 +49,24 @@ pub enum IdeviceErrorCode {
|
||||
UnknownAfcOpcode = -42,
|
||||
InvalidAfcMagic = -43,
|
||||
AfcMissingAttribute = -44,
|
||||
ServiceNotFound = -45,
|
||||
PairingDialogResponsePending = -46,
|
||||
UserDeniedPairing = -47,
|
||||
PasswordProtected = -48,
|
||||
CrashReportMoverBadResponse = -49,
|
||||
UnknownFrame = -50,
|
||||
UnknownHttpSetting = -51,
|
||||
UninitializedStreamId = -52,
|
||||
UnknownXpcType = -53,
|
||||
MalformedXpc = -54,
|
||||
InvalidXpcMagic = -55,
|
||||
UnexpectedXpcVersion = -56,
|
||||
InvalidCString = -57,
|
||||
HttpStreamReset = -58,
|
||||
HttpGoAway = -59,
|
||||
|
||||
// FFI specific bindings
|
||||
AdapterIOFailed = -996,
|
||||
ServiceNotFound = -997,
|
||||
BufferTooSmall = -998,
|
||||
InvalidString = -999,
|
||||
InvalidArg = -1000,
|
||||
@@ -90,7 +104,6 @@ impl From<IdeviceError> for IdeviceErrorCode {
|
||||
IdeviceError::ImageNotMounted => IdeviceErrorCode::ImageNotMounted,
|
||||
IdeviceError::Reqwest(_) => IdeviceErrorCode::Reqwest,
|
||||
IdeviceError::InternalError(_) => IdeviceErrorCode::InternalError,
|
||||
IdeviceError::Xpc(_) => IdeviceErrorCode::Xpc,
|
||||
IdeviceError::NsKeyedArchiveError(_) => IdeviceErrorCode::NsKeyedArchiveError,
|
||||
IdeviceError::UnknownAuxValueType(_) => IdeviceErrorCode::UnknownAuxValueType,
|
||||
IdeviceError::UnknownChannel(_) => IdeviceErrorCode::UnknownChannel,
|
||||
@@ -109,6 +122,25 @@ impl From<IdeviceError> for IdeviceErrorCode {
|
||||
IdeviceError::UnknownAfcOpcode => IdeviceErrorCode::UnknownAfcOpcode,
|
||||
IdeviceError::InvalidAfcMagic => IdeviceErrorCode::InvalidAfcMagic,
|
||||
IdeviceError::AfcMissingAttribute => IdeviceErrorCode::AfcMissingAttribute,
|
||||
IdeviceError::ServiceNotFound => IdeviceErrorCode::ServiceNotFound,
|
||||
IdeviceError::PairingDialogResponsePending => {
|
||||
IdeviceErrorCode::PairingDialogResponsePending
|
||||
}
|
||||
IdeviceError::UserDeniedPairing => IdeviceErrorCode::UserDeniedPairing,
|
||||
IdeviceError::PasswordProtected => IdeviceErrorCode::PasswordProtected,
|
||||
IdeviceError::CrashReportMoverBadResponse(_) => {
|
||||
IdeviceErrorCode::CrashReportMoverBadResponse
|
||||
}
|
||||
IdeviceError::UnknownFrame(_) => IdeviceErrorCode::UnknownFrame,
|
||||
IdeviceError::UnknownHttpSetting(_) => IdeviceErrorCode::UnknownHttpSetting,
|
||||
IdeviceError::UninitializedStreamId => IdeviceErrorCode::UninitializedStreamId,
|
||||
IdeviceError::UnknownXpcType(_) => IdeviceErrorCode::UnknownXpcType,
|
||||
IdeviceError::MalformedXpc => IdeviceErrorCode::MalformedXpc,
|
||||
IdeviceError::InvalidXpcMagic => IdeviceErrorCode::InvalidXpcMagic,
|
||||
IdeviceError::UnexpectedXpcVersion => IdeviceErrorCode::UnexpectedXpcVersion,
|
||||
IdeviceError::InvalidCString => IdeviceErrorCode::InvalidCString,
|
||||
IdeviceError::HttpStreamReset => IdeviceErrorCode::HttpStreamReset,
|
||||
IdeviceError::HttpGoAway(_) => IdeviceErrorCode::HttpGoAway,
|
||||
_ => IdeviceErrorCode::InternalError,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use idevice::{IdeviceError, IdeviceService, heartbeat::HeartbeatClient};
|
||||
|
||||
use crate::{
|
||||
IdeviceErrorCode, IdeviceHandle, RUNTIME,
|
||||
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
|
||||
use idevice::{
|
||||
IdeviceError, IdeviceService, heartbeat::HeartbeatClient, provider::IdeviceProvider,
|
||||
};
|
||||
|
||||
use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle};
|
||||
|
||||
pub struct HeartbeatClientHandle(pub HeartbeatClient);
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct plist_t;
|
||||
|
||||
/// Automatically creates and connects to Installation Proxy, returning a client handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A TcpProvider
|
||||
/// * [`provider`] - An IdeviceProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle
|
||||
///
|
||||
/// # Returns
|
||||
@@ -24,8 +21,8 @@ pub struct plist_t;
|
||||
/// `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 heartbeat_connect_tcp(
|
||||
provider: *mut TcpProviderHandle,
|
||||
pub unsafe extern "C" fn heartbeat_connect(
|
||||
provider: *mut IdeviceProviderHandle,
|
||||
client: *mut *mut HeartbeatClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || client.is_null() {
|
||||
@@ -34,18 +31,9 @@ pub unsafe extern "C" fn heartbeat_connect_tcp(
|
||||
}
|
||||
|
||||
let res: Result<HeartbeatClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the provider (without immediately dropping it)
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let provider_ref = &provider_box.0;
|
||||
|
||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||
// Connect using the reference
|
||||
let result = HeartbeatClient::connect(provider_ref).await;
|
||||
|
||||
// Explicitly keep the provider_box alive until after connect completes
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
HeartbeatClient::connect(provider_ref).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -63,53 +51,6 @@ pub unsafe extern "C" fn heartbeat_connect_tcp(
|
||||
}
|
||||
}
|
||||
|
||||
/// Automatically creates and connects to Installation Proxy, returning a client handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A UsbmuxdProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # 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 heartbeat_connect_usbmuxd(
|
||||
provider: *mut UsbmuxdProviderHandle,
|
||||
client: *mut *mut HeartbeatClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() {
|
||||
log::error!("Provider is null");
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<HeartbeatClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the provider (without immediately dropping it)
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let provider_ref = &provider_box.0;
|
||||
|
||||
// Connect using the reference
|
||||
let result = HeartbeatClient::connect(provider_ref).await;
|
||||
|
||||
// Explicitly keep the provider_box alive until after connect completes
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
let boxed = Box::new(HeartbeatClientHandle(r));
|
||||
unsafe { *client = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Automatically creates and connects to Installation Proxy, returning a client handle
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -120,14 +61,15 @@ pub unsafe extern "C" fn heartbeat_connect_usbmuxd(
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `socket` must be a valid pointer to a handle allocated by this library
|
||||
/// `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 heartbeat_new(
|
||||
socket: *mut IdeviceHandle,
|
||||
client: *mut *mut HeartbeatClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if socket.is_null() {
|
||||
if socket.is_null() || client.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
let socket = unsafe { Box::from_raw(socket) }.0;
|
||||
@@ -151,16 +93,12 @@ pub unsafe extern "C" fn heartbeat_new(
|
||||
pub unsafe extern "C" fn heartbeat_send_polo(
|
||||
client: *mut HeartbeatClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if client.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the client
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let client_ref = &mut client_box.0;
|
||||
let res = client_ref.send_polo().await;
|
||||
|
||||
std::mem::forget(client_box);
|
||||
res
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.send_polo().await
|
||||
});
|
||||
match res {
|
||||
Ok(_) => IdeviceErrorCode::IdeviceSuccess,
|
||||
@@ -186,16 +124,12 @@ pub unsafe extern "C" fn heartbeat_get_marco(
|
||||
interval: u64,
|
||||
new_interval: *mut u64,
|
||||
) -> IdeviceErrorCode {
|
||||
if client.is_null() || new_interval.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
let res: Result<u64, IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the client
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let client_ref = &mut client_box.0;
|
||||
let new = client_ref.get_marco(interval).await;
|
||||
|
||||
std::mem::forget(client_box);
|
||||
new
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.get_marco(interval).await
|
||||
});
|
||||
match res {
|
||||
Ok(n) => {
|
||||
|
||||
@@ -2,20 +2,19 @@
|
||||
|
||||
use std::ffi::c_void;
|
||||
|
||||
use idevice::{IdeviceError, IdeviceService, installation_proxy::InstallationProxyClient};
|
||||
|
||||
use crate::{
|
||||
IdeviceErrorCode, IdeviceHandle, RUNTIME,
|
||||
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
|
||||
util,
|
||||
use idevice::{
|
||||
IdeviceError, IdeviceService, installation_proxy::InstallationProxyClient,
|
||||
provider::IdeviceProvider,
|
||||
};
|
||||
|
||||
use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle, util};
|
||||
|
||||
pub struct InstallationProxyClientHandle(pub InstallationProxyClient);
|
||||
|
||||
/// Automatically creates and connects to Installation Proxy, returning a client handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A TcpProvider
|
||||
/// * [`provider`] - An IdeviceProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle
|
||||
///
|
||||
/// # Returns
|
||||
@@ -26,7 +25,7 @@ pub struct InstallationProxyClientHandle(pub InstallationProxyClient);
|
||||
/// `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 installation_proxy_connect_tcp(
|
||||
provider: *mut TcpProviderHandle,
|
||||
provider: *mut IdeviceProviderHandle,
|
||||
client: *mut *mut InstallationProxyClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || client.is_null() {
|
||||
@@ -35,70 +34,8 @@ pub unsafe extern "C" fn installation_proxy_connect_tcp(
|
||||
}
|
||||
|
||||
let res: Result<InstallationProxyClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the provider (without immediately dropping it)
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let provider_ref = &provider_box.0;
|
||||
|
||||
// Connect using the reference
|
||||
let result = InstallationProxyClient::connect(provider_ref).await;
|
||||
|
||||
// Explicitly keep the provider_box alive until after connect completes
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
let boxed = Box::new(InstallationProxyClientHandle(r));
|
||||
unsafe { *client = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => {
|
||||
// If connection failed, the provider_box was already forgotten,
|
||||
// so we need to reconstruct it to avoid leak
|
||||
let _ = unsafe { Box::from_raw(provider) };
|
||||
e.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Automatically creates and connects to Installation Proxy, returning a client handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A UsbmuxdProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # 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 installation_proxy_connect_usbmuxd(
|
||||
provider: *mut UsbmuxdProviderHandle,
|
||||
client: *mut *mut InstallationProxyClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() {
|
||||
log::error!("Provider is null");
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<InstallationProxyClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the provider (without immediately dropping it)
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let provider_ref = &provider_box.0;
|
||||
|
||||
// Connect using the reference
|
||||
let result = InstallationProxyClient::connect(provider_ref).await;
|
||||
|
||||
// Explicitly keep the provider_box alive until after connect completes
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||
InstallationProxyClient::connect(provider_ref).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -121,14 +58,15 @@ pub unsafe extern "C" fn installation_proxy_connect_usbmuxd(
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `socket` must be a valid pointer to a handle allocated by this library
|
||||
/// `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 installation_proxy_new(
|
||||
socket: *mut IdeviceHandle,
|
||||
client: *mut *mut InstallationProxyClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if socket.is_null() {
|
||||
if socket.is_null() || client.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
let socket = unsafe { Box::from_raw(socket) }.0;
|
||||
@@ -201,11 +139,10 @@ pub unsafe extern "C" fn installation_proxy_get_apps(
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
Ok(mut r) => {
|
||||
let ptr = r.as_mut_ptr();
|
||||
let len = r.len();
|
||||
let boxed_slice = r.into_boxed_slice();
|
||||
let ptr = boxed_slice.as_ptr();
|
||||
std::mem::forget(boxed_slice);
|
||||
std::mem::forget(r);
|
||||
|
||||
unsafe {
|
||||
*out_result = ptr as *mut c_void;
|
||||
@@ -637,11 +574,10 @@ pub unsafe extern "C" fn installation_proxy_browse(
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
Ok(mut r) => {
|
||||
let ptr = r.as_mut_ptr();
|
||||
let len = r.len();
|
||||
let boxed_slice = r.into_boxed_slice();
|
||||
let ptr = boxed_slice.as_ptr();
|
||||
std::mem::forget(boxed_slice);
|
||||
std::mem::forget(r);
|
||||
|
||||
unsafe {
|
||||
*out_result = ptr as *mut c_void;
|
||||
|
||||
@@ -1,33 +1,50 @@
|
||||
// Jackson Coxson
|
||||
|
||||
#[cfg(feature = "tunnel_tcp_stack")]
|
||||
pub mod adapter;
|
||||
#[cfg(feature = "afc")]
|
||||
pub mod afc;
|
||||
#[cfg(feature = "amfi")]
|
||||
pub mod amfi;
|
||||
#[cfg(feature = "core_device_proxy")]
|
||||
pub mod core_device_proxy;
|
||||
#[cfg(feature = "debug_proxy")]
|
||||
pub mod debug_proxy;
|
||||
mod errors;
|
||||
#[cfg(feature = "heartbeat")]
|
||||
pub mod heartbeat;
|
||||
#[cfg(feature = "installation_proxy")]
|
||||
pub mod installation_proxy;
|
||||
#[cfg(feature = "location_simulation")]
|
||||
pub mod location_simulation;
|
||||
pub mod lockdown;
|
||||
pub mod logging;
|
||||
#[cfg(feature = "misagent")]
|
||||
pub mod misagent;
|
||||
pub mod mounter;
|
||||
#[cfg(feature = "mobile_image_mounter")]
|
||||
pub mod mobile_image_mounter;
|
||||
#[cfg(feature = "os_trace_relay")]
|
||||
pub mod os_trace_relay;
|
||||
mod pairing_file;
|
||||
#[cfg(feature = "dvt")]
|
||||
pub mod process_control;
|
||||
pub mod provider;
|
||||
#[cfg(feature = "dvt")]
|
||||
pub mod remote_server;
|
||||
pub mod remotexpc;
|
||||
pub mod sbservices;
|
||||
#[cfg(feature = "xpc")]
|
||||
pub mod rsd;
|
||||
#[cfg(feature = "springboardservices")]
|
||||
pub mod springboardservices;
|
||||
#[cfg(feature = "syslog_relay")]
|
||||
pub mod syslog_relay;
|
||||
#[cfg(feature = "usbmuxd")]
|
||||
pub mod usbmuxd;
|
||||
pub mod util;
|
||||
|
||||
pub use errors::*;
|
||||
pub use pairing_file::*;
|
||||
|
||||
use idevice::{Idevice, IdeviceSocket};
|
||||
use idevice::{Idevice, IdeviceSocket, ReadWrite};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::ffi::{CStr, CString, c_char};
|
||||
use tokio::runtime::{self, Runtime};
|
||||
@@ -42,6 +59,11 @@ static RUNTIME: Lazy<Runtime> = Lazy::new(|| {
|
||||
|
||||
pub const LOCKDOWN_PORT: u16 = 62078;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ReadWriteOpaque {
|
||||
pub inner: Option<Box<dyn ReadWrite>>,
|
||||
}
|
||||
|
||||
/// Opaque C-compatible handle to an Idevice connection
|
||||
pub struct IdeviceHandle(pub Idevice);
|
||||
pub struct IdeviceSocketHandle(IdeviceSocket);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use idevice::{dvt::location_simulation::LocationSimulationClient, tcp::adapter::Adapter};
|
||||
use idevice::{ReadWrite, dvt::location_simulation::LocationSimulationClient};
|
||||
|
||||
use crate::{IdeviceErrorCode, RUNTIME, remote_server::RemoteServerAdapterHandle};
|
||||
use crate::{IdeviceErrorCode, RUNTIME, remote_server::RemoteServerHandle};
|
||||
|
||||
/// Opaque handle to a ProcessControlClient
|
||||
pub struct LocationSimulationAdapterHandle<'a>(pub LocationSimulationClient<'a, Adapter>);
|
||||
pub struct LocationSimulationHandle<'a>(pub LocationSimulationClient<'a, Box<dyn ReadWrite>>);
|
||||
|
||||
/// Creates a new ProcessControlClient from a RemoteServerClient
|
||||
///
|
||||
@@ -21,8 +21,8 @@ pub struct LocationSimulationAdapterHandle<'a>(pub LocationSimulationClient<'a,
|
||||
/// `handle` must be a valid pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn location_simulation_new(
|
||||
server: *mut RemoteServerAdapterHandle,
|
||||
handle: *mut *mut LocationSimulationAdapterHandle<'static>,
|
||||
server: *mut RemoteServerHandle,
|
||||
handle: *mut *mut LocationSimulationHandle<'static>,
|
||||
) -> IdeviceErrorCode {
|
||||
if server.is_null() || handle.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
@@ -33,7 +33,7 @@ pub unsafe extern "C" fn location_simulation_new(
|
||||
|
||||
match res {
|
||||
Ok(client) => {
|
||||
let boxed = Box::new(LocationSimulationAdapterHandle(client));
|
||||
let boxed = Box::new(LocationSimulationHandle(client));
|
||||
unsafe { *handle = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
@@ -49,9 +49,7 @@ pub unsafe extern "C" fn location_simulation_new(
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library or NULL
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn location_simulation_free(
|
||||
handle: *mut LocationSimulationAdapterHandle<'static>,
|
||||
) {
|
||||
pub unsafe extern "C" fn location_simulation_free(handle: *mut LocationSimulationHandle<'static>) {
|
||||
if !handle.is_null() {
|
||||
let _ = unsafe { Box::from_raw(handle) };
|
||||
}
|
||||
@@ -69,7 +67,7 @@ pub unsafe extern "C" fn location_simulation_free(
|
||||
/// All pointers must be valid or NULL where appropriate
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn location_simulation_clear(
|
||||
handle: *mut LocationSimulationAdapterHandle<'static>,
|
||||
handle: *mut LocationSimulationHandle<'static>,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
@@ -98,7 +96,7 @@ pub unsafe extern "C" fn location_simulation_clear(
|
||||
/// All pointers must be valid or NULL where appropriate
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn location_simulation_set(
|
||||
handle: *mut LocationSimulationAdapterHandle<'static>,
|
||||
handle: *mut LocationSimulationHandle<'static>,
|
||||
latitude: f64,
|
||||
longitude: f64,
|
||||
) -> IdeviceErrorCode {
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
use std::ffi::c_void;
|
||||
|
||||
use idevice::{IdeviceError, IdeviceService, lockdown::LockdownClient};
|
||||
use idevice::{IdeviceError, IdeviceService, lockdown::LockdownClient, provider::IdeviceProvider};
|
||||
|
||||
use crate::{
|
||||
IdeviceErrorCode, IdeviceHandle, IdevicePairingFile, RUNTIME,
|
||||
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
|
||||
IdeviceErrorCode, IdeviceHandle, IdevicePairingFile, RUNTIME, provider::IdeviceProviderHandle,
|
||||
};
|
||||
|
||||
pub struct LockdowndClientHandle(pub LockdownClient);
|
||||
@@ -14,7 +13,7 @@ pub struct LockdowndClientHandle(pub LockdownClient);
|
||||
/// Connects to lockdownd service using TCP provider
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A TcpProvider
|
||||
/// * [`provider`] - An IdeviceProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle
|
||||
///
|
||||
/// # Returns
|
||||
@@ -24,8 +23,8 @@ pub struct LockdowndClientHandle(pub LockdownClient);
|
||||
/// `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 lockdownd_connect_tcp(
|
||||
provider: *mut TcpProviderHandle,
|
||||
pub unsafe extern "C" fn lockdownd_connect(
|
||||
provider: *mut IdeviceProviderHandle,
|
||||
client: *mut *mut LockdowndClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || client.is_null() {
|
||||
@@ -34,11 +33,8 @@ pub unsafe extern "C" fn lockdownd_connect_tcp(
|
||||
}
|
||||
|
||||
let res: Result<LockdownClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
let provider_ref = &provider_box.0;
|
||||
let result = LockdownClient::connect(provider_ref).await;
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||
LockdownClient::connect(provider_ref).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -54,57 +50,18 @@ pub unsafe extern "C" fn lockdownd_connect_tcp(
|
||||
}
|
||||
}
|
||||
|
||||
/// Connects to lockdownd service using Usbmuxd provider
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A UsbmuxdProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # 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 lockdownd_connect_usbmuxd(
|
||||
provider: *mut UsbmuxdProviderHandle,
|
||||
client: *mut *mut LockdowndClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || client.is_null() {
|
||||
log::error!("Null pointer provided");
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<LockdownClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
let provider_ref = &provider_box.0;
|
||||
let result = LockdownClient::connect(provider_ref).await;
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
let boxed = Box::new(LockdowndClientHandle(r));
|
||||
unsafe { *client = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new LockdowndClient from an existing Idevice connection
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`socket`] - An IdeviceSocket handle
|
||||
/// * [`socket`] - An IdeviceSocket handle.
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `socket` must be a valid pointer to a handle allocated by this library
|
||||
/// `socket` must be a valid pointer to a handle allocated by this library. The socket is consumed,
|
||||
/// and maybe 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 lockdownd_new(
|
||||
@@ -139,15 +96,10 @@ pub unsafe extern "C" fn lockdownd_start_session(
|
||||
pairing_file: *mut IdevicePairingFile,
|
||||
) -> IdeviceErrorCode {
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let pairing_file = unsafe { Box::from_raw(pairing_file) };
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
let pairing_file_ref = unsafe { &(*pairing_file).0 };
|
||||
|
||||
let client_ref = &mut client_box.0;
|
||||
let res = client_ref.start_session(&pairing_file.0).await;
|
||||
|
||||
std::mem::forget(client_box);
|
||||
std::mem::forget(pairing_file);
|
||||
res
|
||||
client_ref.start_session(pairing_file_ref).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -187,11 +139,8 @@ pub unsafe extern "C" fn lockdownd_start_service(
|
||||
.into_owned();
|
||||
|
||||
let res: Result<(u16, bool), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let res = client_ref.start_service(identifier).await;
|
||||
std::mem::forget(client_box);
|
||||
res
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.start_service(identifier).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -247,11 +196,8 @@ pub unsafe extern "C" fn lockdownd_get_value(
|
||||
};
|
||||
|
||||
let res: Result<plist::Value, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let res = client_ref.get_value(value, domain).await;
|
||||
std::mem::forget(client_box);
|
||||
res
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.get_value(value, domain).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -287,11 +233,8 @@ pub unsafe extern "C" fn lockdownd_get_all_values(
|
||||
}
|
||||
|
||||
let res: Result<plist::Dictionary, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let res = client_ref.get_all_values().await;
|
||||
std::mem::forget(client_box);
|
||||
res
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.get_all_values().await
|
||||
});
|
||||
|
||||
match res {
|
||||
|
||||
@@ -2,19 +2,16 @@
|
||||
//!
|
||||
//! Provides C-compatible bindings for interacting with the misagent service on iOS devices.
|
||||
|
||||
use idevice::{IdeviceError, IdeviceService, misagent::MisagentClient};
|
||||
use idevice::{IdeviceError, IdeviceService, misagent::MisagentClient, provider::IdeviceProvider};
|
||||
|
||||
use crate::{
|
||||
IdeviceErrorCode, RUNTIME,
|
||||
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
|
||||
};
|
||||
use crate::{IdeviceErrorCode, RUNTIME, provider::IdeviceProviderHandle};
|
||||
|
||||
pub struct MisagentClientHandle(pub MisagentClient);
|
||||
|
||||
/// Automatically creates and connects to Misagent, returning a client handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A TcpProvider
|
||||
/// * [`provider`] - An IdeviceProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated MisagentClient handle
|
||||
///
|
||||
/// # Returns
|
||||
@@ -24,8 +21,8 @@ pub struct MisagentClientHandle(pub MisagentClient);
|
||||
/// `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 misagent_connect_tcp(
|
||||
provider: *mut TcpProviderHandle,
|
||||
pub unsafe extern "C" fn misagent_connect(
|
||||
provider: *mut IdeviceProviderHandle,
|
||||
client: *mut *mut MisagentClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || client.is_null() {
|
||||
@@ -34,70 +31,8 @@ pub unsafe extern "C" fn misagent_connect_tcp(
|
||||
}
|
||||
|
||||
let res: Result<MisagentClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the provider (without immediately dropping it)
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let provider_ref = &provider_box.0;
|
||||
|
||||
// Connect using the reference
|
||||
let result = MisagentClient::connect(provider_ref).await;
|
||||
|
||||
// Explicitly keep the provider_box alive until after connect completes
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
let boxed = Box::new(MisagentClientHandle(r));
|
||||
unsafe { *client = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => {
|
||||
// If connection failed, the provider_box was already forgotten,
|
||||
// so we need to reconstruct it to avoid leak
|
||||
let _ = unsafe { Box::from_raw(provider) };
|
||||
e.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Automatically creates and connects to Misagent, returning a client handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A UsbmuxdProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated MisagentClient handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # 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 misagent_connect_usbmuxd(
|
||||
provider: *mut UsbmuxdProviderHandle,
|
||||
client: *mut *mut MisagentClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() {
|
||||
log::error!("Provider is null");
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<MisagentClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the provider (without immediately dropping it)
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let provider_ref = &provider_box.0;
|
||||
|
||||
// Connect using the reference
|
||||
let result = MisagentClient::connect(provider_ref).await;
|
||||
|
||||
// Explicitly keep the provider_box alive until after connect completes
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||
MisagentClient::connect(provider_ref).await
|
||||
});
|
||||
|
||||
match res {
|
||||
|
||||
@@ -2,21 +2,19 @@
|
||||
|
||||
use std::ffi::c_void;
|
||||
|
||||
use idevice::{IdeviceError, IdeviceService, mobile_image_mounter::ImageMounter};
|
||||
use idevice::{
|
||||
IdeviceError, IdeviceService, mobile_image_mounter::ImageMounter, provider::IdeviceProvider,
|
||||
};
|
||||
use plist::Value;
|
||||
|
||||
use crate::{
|
||||
IdeviceErrorCode, IdeviceHandle, RUNTIME,
|
||||
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
|
||||
util,
|
||||
};
|
||||
use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle, util};
|
||||
|
||||
pub struct ImageMounterHandle(pub ImageMounter);
|
||||
|
||||
/// Connects to the Image Mounter service using a TCP provider
|
||||
/// Connects to the Image Mounter service using a provider
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A TcpProvider
|
||||
/// * [`provider`] - An IdeviceProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle
|
||||
///
|
||||
/// # Returns
|
||||
@@ -26,8 +24,8 @@ pub struct ImageMounterHandle(pub ImageMounter);
|
||||
/// `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 image_mounter_connect_tcp(
|
||||
provider: *mut TcpProviderHandle,
|
||||
pub unsafe extern "C" fn image_mounter_connect(
|
||||
provider: *mut IdeviceProviderHandle,
|
||||
client: *mut *mut ImageMounterHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || client.is_null() {
|
||||
@@ -36,11 +34,8 @@ pub unsafe extern "C" fn image_mounter_connect_tcp(
|
||||
}
|
||||
|
||||
let res: Result<ImageMounter, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
let provider_ref = &provider_box.0;
|
||||
let result = ImageMounter::connect(provider_ref).await;
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||
ImageMounter::connect(provider_ref).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -56,46 +51,6 @@ pub unsafe extern "C" fn image_mounter_connect_tcp(
|
||||
}
|
||||
}
|
||||
|
||||
/// Connects to the Image Mounter service using a Usbmuxd provider
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A UsbmuxdProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # 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 image_mounter_connect_usbmuxd(
|
||||
provider: *mut UsbmuxdProviderHandle,
|
||||
client: *mut *mut ImageMounterHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() {
|
||||
log::error!("Provider is null");
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<ImageMounter, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
let provider_ref = &provider_box.0;
|
||||
let result = ImageMounter::connect(provider_ref).await;
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
let boxed = Box::new(ImageMounterHandle(r));
|
||||
unsafe { *client = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new ImageMounter client from an existing Idevice connection
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -159,11 +114,8 @@ pub unsafe extern "C" fn image_mounter_copy_devices(
|
||||
devices_len: *mut libc::size_t,
|
||||
) -> IdeviceErrorCode {
|
||||
let res: Result<Vec<Value>, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.copy_devices().await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.copy_devices().await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -219,11 +171,8 @@ pub unsafe extern "C" fn image_mounter_lookup_image(
|
||||
};
|
||||
|
||||
let res: Result<Vec<u8>, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.lookup_image(image_type).await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.lookup_image(image_type).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -279,13 +228,10 @@ pub unsafe extern "C" fn image_mounter_upload_image(
|
||||
let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) };
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref
|
||||
.upload_image(image_type, image_slice, signature_slice.to_vec())
|
||||
.await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
.await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -349,18 +295,15 @@ pub unsafe extern "C" fn image_mounter_mount_image(
|
||||
};
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref
|
||||
.mount_image(
|
||||
image_type,
|
||||
signature_slice.to_vec(),
|
||||
trust_cache,
|
||||
info_plist,
|
||||
)
|
||||
.await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
.await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -397,11 +340,8 @@ pub unsafe extern "C" fn image_mounter_unmount_image(
|
||||
};
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.unmount_image(mount_path).await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.unmount_image(mount_path).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -432,11 +372,8 @@ pub unsafe extern "C" fn image_mounter_query_developer_mode_status(
|
||||
}
|
||||
|
||||
let res: Result<bool, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.query_developer_mode_status().await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.query_developer_mode_status().await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -478,13 +415,10 @@ pub unsafe extern "C" fn image_mounter_mount_developer(
|
||||
let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) };
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref
|
||||
.mount_developer(image_slice, signature_slice.to_vec())
|
||||
.await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
.await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -531,13 +465,10 @@ pub unsafe extern "C" fn image_mounter_query_personalization_manifest(
|
||||
let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) };
|
||||
|
||||
let res: Result<Vec<u8>, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref
|
||||
.query_personalization_manifest(image_type, signature_slice.to_vec())
|
||||
.await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
.await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -590,11 +521,8 @@ pub unsafe extern "C" fn image_mounter_query_nonce(
|
||||
};
|
||||
|
||||
let res: Result<Vec<u8>, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.query_nonce(image_type).await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.query_nonce(image_type).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -645,13 +573,10 @@ pub unsafe extern "C" fn image_mounter_query_personalization_identifiers(
|
||||
};
|
||||
|
||||
let res: Result<plist::Dictionary, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref
|
||||
.query_personalization_identifiers(image_type)
|
||||
.await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
.await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -679,11 +604,8 @@ pub unsafe extern "C" fn image_mounter_roll_personalization_nonce(
|
||||
client: *mut ImageMounterHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.roll_personalization_nonce().await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.roll_personalization_nonce().await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -707,11 +629,8 @@ pub unsafe extern "C" fn image_mounter_roll_cryptex_nonce(
|
||||
client: *mut ImageMounterHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let result = client_ref.roll_cryptex_nonce().await;
|
||||
std::mem::forget(client_box);
|
||||
result
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
client_ref.roll_cryptex_nonce().await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -739,10 +658,11 @@ pub unsafe extern "C" fn image_mounter_roll_cryptex_nonce(
|
||||
///
|
||||
/// # Safety
|
||||
/// All pointers must be valid (except optional ones which can be null)
|
||||
#[cfg(feature = "tss")]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd(
|
||||
pub unsafe extern "C" fn image_mounter_mount_personalized(
|
||||
client: *mut ImageMounterHandle,
|
||||
provider: *mut UsbmuxdProviderHandle,
|
||||
provider: *mut IdeviceProviderHandle,
|
||||
image: *const u8,
|
||||
image_len: libc::size_t,
|
||||
trust_cache: *const u8,
|
||||
@@ -772,11 +692,9 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd(
|
||||
};
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let provider_ref = &provider_box.0;
|
||||
let result = client_ref
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||
client_ref
|
||||
.mount_personalized(
|
||||
provider_ref,
|
||||
image_slice.to_vec(),
|
||||
@@ -785,87 +703,7 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd(
|
||||
info_plist,
|
||||
unique_chip_id,
|
||||
)
|
||||
.await;
|
||||
std::mem::forget(client_box);
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(_) => IdeviceErrorCode::IdeviceSuccess,
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Mounts a personalized developer image
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`client`] - A valid ImageMounter handle
|
||||
/// * [`provider`] - A valid provider handle
|
||||
/// * [`image`] - Pointer to the image data
|
||||
/// * [`image_len`] - Length of the image data
|
||||
/// * [`trust_cache`] - Pointer to the trust cache data
|
||||
/// * [`trust_cache_len`] - Length of the trust cache data
|
||||
/// * [`build_manifest`] - Pointer to the build manifest data
|
||||
/// * [`build_manifest_len`] - Length of the build manifest data
|
||||
/// * [`info_plist`] - Pointer to info plist (optional)
|
||||
/// * [`unique_chip_id`] - The device's unique chip ID
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// All pointers must be valid (except optional ones which can be null)
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn image_mounter_mount_personalized_tcp(
|
||||
client: *mut ImageMounterHandle,
|
||||
provider: *mut TcpProviderHandle,
|
||||
image: *const u8,
|
||||
image_len: libc::size_t,
|
||||
trust_cache: *const u8,
|
||||
trust_cache_len: libc::size_t,
|
||||
build_manifest: *const u8,
|
||||
build_manifest_len: libc::size_t,
|
||||
info_plist: *const c_void,
|
||||
unique_chip_id: u64,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || image.is_null() || trust_cache.is_null() || build_manifest.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let image_slice = unsafe { std::slice::from_raw_parts(image, image_len) };
|
||||
let trust_cache_slice = unsafe { std::slice::from_raw_parts(trust_cache, trust_cache_len) };
|
||||
let build_manifest_slice =
|
||||
unsafe { std::slice::from_raw_parts(build_manifest, build_manifest_len) };
|
||||
|
||||
let info_plist = if !info_plist.is_null() {
|
||||
Some(
|
||||
unsafe { Box::from_raw(info_plist as *mut Value) }
|
||||
.as_ref()
|
||||
.clone(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let provider_ref = &provider_box.0;
|
||||
let result = client_ref
|
||||
.mount_personalized(
|
||||
provider_ref,
|
||||
image_slice.to_vec(),
|
||||
trust_cache_slice.to_vec(),
|
||||
build_manifest_slice,
|
||||
info_plist,
|
||||
unique_chip_id,
|
||||
)
|
||||
.await;
|
||||
std::mem::forget(client_box);
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
.await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -895,10 +733,11 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_tcp(
|
||||
///
|
||||
/// # Safety
|
||||
/// All pointers must be valid (except optional ones which can be null)
|
||||
#[cfg(feature = "tss")]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd_with_callback(
|
||||
pub unsafe extern "C" fn image_mounter_mount_personalized_with_callback(
|
||||
client: *mut ImageMounterHandle,
|
||||
provider: *mut UsbmuxdProviderHandle,
|
||||
provider: *mut IdeviceProviderHandle,
|
||||
image: *const u8,
|
||||
image_len: libc::size_t,
|
||||
trust_cache: *const u8,
|
||||
@@ -930,16 +769,14 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd_with_callback(
|
||||
};
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let provider_ref = &provider_box.0;
|
||||
let client_ref = unsafe { &mut (*client).0 };
|
||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||
|
||||
let callback_wrapper = |((progress, total), context)| async move {
|
||||
callback(progress, total, context);
|
||||
};
|
||||
|
||||
let result = client_ref
|
||||
client_ref
|
||||
.mount_personalized_with_callback(
|
||||
provider_ref,
|
||||
image_slice.to_vec(),
|
||||
@@ -950,98 +787,7 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd_with_callback(
|
||||
callback_wrapper,
|
||||
context,
|
||||
)
|
||||
.await;
|
||||
std::mem::forget(client_box);
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(_) => IdeviceErrorCode::IdeviceSuccess,
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Mounts a personalized developer image with progress callback
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`client`] - A valid ImageMounter handle
|
||||
/// * [`provider`] - A valid provider handle
|
||||
/// * [`image`] - Pointer to the image data
|
||||
/// * [`image_len`] - Length of the image data
|
||||
/// * [`trust_cache`] - Pointer to the trust cache data
|
||||
/// * [`trust_cache_len`] - Length of the trust cache data
|
||||
/// * [`build_manifest`] - Pointer to the build manifest data
|
||||
/// * [`build_manifest_len`] - Length of the build manifest data
|
||||
/// * [`info_plist`] - Pointer to info plist (optional)
|
||||
/// * [`unique_chip_id`] - The device's unique chip ID
|
||||
/// * [`callback`] - Progress callback function
|
||||
/// * [`context`] - User context to pass to callback
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// All pointers must be valid (except optional ones which can be null)
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn image_mounter_mount_personalized_tcp_with_callback(
|
||||
client: *mut ImageMounterHandle,
|
||||
provider: *mut TcpProviderHandle,
|
||||
image: *const u8,
|
||||
image_len: libc::size_t,
|
||||
trust_cache: *const u8,
|
||||
trust_cache_len: libc::size_t,
|
||||
build_manifest: *const u8,
|
||||
build_manifest_len: libc::size_t,
|
||||
info_plist: *const c_void,
|
||||
unique_chip_id: u64,
|
||||
callback: extern "C" fn(progress: libc::size_t, total: libc::size_t, context: *mut c_void),
|
||||
context: *mut c_void,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || image.is_null() || trust_cache.is_null() || build_manifest.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let image_slice = unsafe { std::slice::from_raw_parts(image, image_len) };
|
||||
let trust_cache_slice = unsafe { std::slice::from_raw_parts(trust_cache, trust_cache_len) };
|
||||
let build_manifest_slice =
|
||||
unsafe { std::slice::from_raw_parts(build_manifest, build_manifest_len) };
|
||||
|
||||
let info_plist = if !info_plist.is_null() {
|
||||
Some(
|
||||
unsafe { Box::from_raw(info_plist as *mut Value) }
|
||||
.as_ref()
|
||||
.clone(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client_box = unsafe { Box::from_raw(client) };
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
let client_ref = &mut client_box.0;
|
||||
let provider_ref = &provider_box.0;
|
||||
|
||||
let callback_wrapper = |((progress, total), context)| async move {
|
||||
callback(progress, total, context);
|
||||
};
|
||||
|
||||
let result = client_ref
|
||||
.mount_personalized_with_callback(
|
||||
provider_ref,
|
||||
image_slice.to_vec(),
|
||||
trust_cache_slice.to_vec(),
|
||||
build_manifest_slice,
|
||||
info_plist,
|
||||
unique_chip_id,
|
||||
callback_wrapper,
|
||||
context,
|
||||
)
|
||||
.await;
|
||||
std::mem::forget(client_box);
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
.await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
use std::ffi::{CStr, c_char};
|
||||
|
||||
use idevice::{dvt::process_control::ProcessControlClient, tcp::adapter::Adapter};
|
||||
use idevice::{ReadWrite, dvt::process_control::ProcessControlClient};
|
||||
use plist::{Dictionary, Value};
|
||||
|
||||
use crate::{IdeviceErrorCode, RUNTIME, remote_server::RemoteServerAdapterHandle};
|
||||
use crate::{IdeviceErrorCode, RUNTIME, remote_server::RemoteServerHandle};
|
||||
|
||||
/// Opaque handle to a ProcessControlClient
|
||||
pub struct ProcessControlAdapterHandle<'a>(pub ProcessControlClient<'a, Adapter>);
|
||||
pub struct ProcessControlHandle<'a>(pub ProcessControlClient<'a, Box<dyn ReadWrite>>);
|
||||
|
||||
/// Creates a new ProcessControlClient from a RemoteServerClient
|
||||
///
|
||||
@@ -24,8 +24,8 @@ pub struct ProcessControlAdapterHandle<'a>(pub ProcessControlClient<'a, Adapter>
|
||||
/// `handle` must be a valid pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn process_control_new(
|
||||
server: *mut RemoteServerAdapterHandle,
|
||||
handle: *mut *mut ProcessControlAdapterHandle<'static>,
|
||||
server: *mut RemoteServerHandle,
|
||||
handle: *mut *mut ProcessControlHandle<'static>,
|
||||
) -> IdeviceErrorCode {
|
||||
if server.is_null() || handle.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
@@ -36,7 +36,7 @@ pub unsafe extern "C" fn process_control_new(
|
||||
|
||||
match res {
|
||||
Ok(client) => {
|
||||
let boxed = Box::new(ProcessControlAdapterHandle(client));
|
||||
let boxed = Box::new(ProcessControlHandle(client));
|
||||
unsafe { *handle = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
@@ -52,7 +52,7 @@ pub unsafe extern "C" fn process_control_new(
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library or NULL
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn process_control_free(handle: *mut ProcessControlAdapterHandle<'static>) {
|
||||
pub unsafe extern "C" fn process_control_free(handle: *mut ProcessControlHandle<'static>) {
|
||||
if !handle.is_null() {
|
||||
let _ = unsafe { Box::from_raw(handle) };
|
||||
}
|
||||
@@ -76,7 +76,7 @@ pub unsafe extern "C" fn process_control_free(handle: *mut ProcessControlAdapter
|
||||
/// All pointers must be valid or NULL where appropriate
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn process_control_launch_app(
|
||||
handle: *mut ProcessControlAdapterHandle<'static>,
|
||||
handle: *mut ProcessControlHandle<'static>,
|
||||
bundle_id: *const c_char,
|
||||
env_vars: *const *const c_char,
|
||||
env_vars_count: usize,
|
||||
@@ -159,7 +159,7 @@ pub unsafe extern "C" fn process_control_launch_app(
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn process_control_kill_app(
|
||||
handle: *mut ProcessControlAdapterHandle<'static>,
|
||||
handle: *mut ProcessControlHandle<'static>,
|
||||
pid: u64,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() {
|
||||
@@ -188,7 +188,7 @@ pub unsafe extern "C" fn process_control_kill_app(
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn process_control_disable_memory_limit(
|
||||
handle: *mut ProcessControlAdapterHandle<'static>,
|
||||
handle: *mut ProcessControlHandle<'static>,
|
||||
pid: u64,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use idevice::provider::{TcpProvider, UsbmuxdProvider};
|
||||
use idevice::provider::{IdeviceProvider, TcpProvider, UsbmuxdProvider};
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use crate::{IdeviceErrorCode, usbmuxd::UsbmuxdAddrHandle, util};
|
||||
|
||||
pub struct TcpProviderHandle(pub TcpProvider);
|
||||
pub struct UsbmuxdProviderHandle(pub UsbmuxdProvider);
|
||||
pub struct IdeviceProviderHandle(pub Box<dyn IdeviceProvider>);
|
||||
|
||||
/// Creates a TCP provider for idevice
|
||||
///
|
||||
@@ -22,16 +21,21 @@ pub struct UsbmuxdProviderHandle(pub UsbmuxdProvider);
|
||||
///
|
||||
/// # Safety
|
||||
/// `ip` must be a valid sockaddr
|
||||
/// `pairing_file` must never be used again
|
||||
/// `pairing_file` is consumed must never be used again
|
||||
/// `label` must be a valid Cstr
|
||||
/// `provider` must be a valid, non-null pointer to a location where the handle will be stored
|
||||
#[cfg(feature = "tcp")]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn idevice_tcp_provider_new(
|
||||
ip: *const libc::sockaddr,
|
||||
pairing_file: *mut crate::pairing_file::IdevicePairingFile,
|
||||
label: *const c_char,
|
||||
provider: *mut *mut TcpProviderHandle,
|
||||
provider: *mut *mut IdeviceProviderHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if ip.is_null() || label.is_null() || provider.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let addr = match util::c_addr_to_rust(ip) {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
@@ -53,23 +57,23 @@ pub unsafe extern "C" fn idevice_tcp_provider_new(
|
||||
label,
|
||||
};
|
||||
|
||||
let boxed = Box::new(TcpProviderHandle(t));
|
||||
let boxed = Box::new(IdeviceProviderHandle(Box::new(t)));
|
||||
unsafe { *provider = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
/// Frees a TcpProvider handle
|
||||
/// Frees an IdeviceProvider handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - The provider handle to free
|
||||
///
|
||||
/// # Safety
|
||||
/// `provider` must be a valid pointer to a TcpProvider handle that was allocated by this library,
|
||||
/// or NULL (in which case this function does nothing)
|
||||
/// `provider` must be a valid pointer to a IdeviceProvider handle that was allocated this library
|
||||
/// or NULL (in which case this function does nothing)
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn tcp_provider_free(provider: *mut TcpProviderHandle) {
|
||||
pub unsafe extern "C" fn idevice_provider_free(provider: *mut IdeviceProviderHandle) {
|
||||
if !provider.is_null() {
|
||||
log::debug!("Freeing TCP provider");
|
||||
log::debug!("Freeing provider");
|
||||
unsafe { drop(Box::from_raw(provider)) };
|
||||
}
|
||||
}
|
||||
@@ -81,7 +85,6 @@ pub unsafe extern "C" fn tcp_provider_free(provider: *mut TcpProviderHandle) {
|
||||
/// * [`tag`] - The tag returned in usbmuxd responses
|
||||
/// * [`udid`] - The UDID of the device to connect to
|
||||
/// * [`device_id`] - The muxer ID of the device to connect to
|
||||
/// * [`pairing_file`] - The pairing file handle to use
|
||||
/// * [`label`] - The label to use with the connection
|
||||
/// * [`provider`] - A pointer to a newly allocated provider
|
||||
///
|
||||
@@ -91,7 +94,6 @@ pub unsafe extern "C" fn tcp_provider_free(provider: *mut TcpProviderHandle) {
|
||||
/// # Safety
|
||||
/// `addr` must be a valid pointer to UsbmuxdAddrHandle created by this library, and never used again
|
||||
/// `udid` must be a valid CStr
|
||||
/// `pairing_file` must never be used again
|
||||
/// `label` must be a valid Cstr
|
||||
/// `provider` must be a valid, non-null pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
@@ -101,21 +103,25 @@ pub unsafe extern "C" fn usbmuxd_provider_new(
|
||||
udid: *const c_char,
|
||||
device_id: u32,
|
||||
label: *const c_char,
|
||||
provider: *mut *mut UsbmuxdProviderHandle,
|
||||
provider: *mut *mut IdeviceProviderHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if addr.is_null() || udid.is_null() || label.is_null() || provider.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let udid = match unsafe { CStr::from_ptr(udid) }.to_str() {
|
||||
Ok(u) => u.to_string(),
|
||||
Err(e) => {
|
||||
log::error!("Invalid UDID string: {e:?}");
|
||||
return IdeviceErrorCode::InvalidArgument;
|
||||
return IdeviceErrorCode::InvalidString;
|
||||
}
|
||||
};
|
||||
|
||||
let label = match unsafe { CStr::from_ptr(label) }.to_str() {
|
||||
Ok(l) => l.to_string(),
|
||||
Err(e) => {
|
||||
log::error!("Invalid UDID string: {e:?}");
|
||||
return IdeviceErrorCode::InvalidArgument;
|
||||
log::error!("Invalid label string: {e:?}");
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -129,23 +135,8 @@ pub unsafe extern "C" fn usbmuxd_provider_new(
|
||||
label,
|
||||
};
|
||||
|
||||
let boxed = Box::new(UsbmuxdProviderHandle(p));
|
||||
let boxed = Box::new(IdeviceProviderHandle(Box::new(p)));
|
||||
unsafe { *provider = Box::into_raw(boxed) };
|
||||
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
/// Frees a UsbmuxdProvider handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - The provider handle to free
|
||||
///
|
||||
/// # Safety
|
||||
/// `provider` must be a valid pointer to a UsbmuxdProvider handle that was allocated by this library,
|
||||
/// or NULL (in which case this function does nothing)
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn usbmuxd_provider_free(provider: *mut UsbmuxdProviderHandle) {
|
||||
if !provider.is_null() {
|
||||
unsafe { drop(Box::from_raw(provider)) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,94 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use crate::core_device_proxy::AdapterHandle;
|
||||
use crate::{IdeviceErrorCode, RUNTIME};
|
||||
use idevice::IdeviceError;
|
||||
use crate::rsd::RsdHandshakeHandle;
|
||||
use crate::{IdeviceErrorCode, RUNTIME, ReadWriteOpaque};
|
||||
use idevice::dvt::remote_server::RemoteServerClient;
|
||||
use idevice::tcp::adapter::Adapter;
|
||||
use idevice::tcp::stream::AdapterStream;
|
||||
use idevice::{IdeviceError, ReadWrite, RsdService};
|
||||
|
||||
/// Opaque handle to a RemoteServerClient
|
||||
pub struct RemoteServerAdapterHandle(pub RemoteServerClient<Adapter>);
|
||||
pub struct RemoteServerHandle(pub RemoteServerClient<Box<dyn ReadWrite>>);
|
||||
|
||||
/// Creates a new RemoteServerClient from a ReadWrite connection
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`connection`] - The connection to use for communication
|
||||
/// * [`socket`] - The connection to use for communication, an object that implements ReadWrite
|
||||
/// * [`handle`] - Pointer to store the newly created RemoteServerClient handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `connection` must be a valid pointer to a handle allocated by this library
|
||||
/// `socket` must be a valid pointer to a handle allocated by this library. It is consumed and may
|
||||
/// not be used again.
|
||||
/// `handle` must be a valid pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn remote_server_adapter_new(
|
||||
adapter: *mut crate::core_device_proxy::AdapterHandle,
|
||||
handle: *mut *mut RemoteServerAdapterHandle,
|
||||
pub unsafe extern "C" fn remote_server_new(
|
||||
socket: *mut ReadWriteOpaque,
|
||||
handle: *mut *mut RemoteServerHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if adapter.is_null() {
|
||||
if socket.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let connection = unsafe { Box::from_raw(adapter) };
|
||||
let wrapper = unsafe { &mut *socket };
|
||||
|
||||
let res: Result<RemoteServerClient<Adapter>, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let mut client = RemoteServerClient::new(connection.0);
|
||||
client.read_message(0).await?; // Until Message has bindings, we'll do the first read
|
||||
Ok(client)
|
||||
});
|
||||
let res: Result<RemoteServerClient<Box<dyn ReadWrite>>, IdeviceError> =
|
||||
match wrapper.inner.take() {
|
||||
Some(stream) => RUNTIME.block_on(async move {
|
||||
let mut client = RemoteServerClient::new(stream);
|
||||
client.read_message(0).await?;
|
||||
Ok(client)
|
||||
}),
|
||||
None => return IdeviceErrorCode::InvalidArg,
|
||||
};
|
||||
|
||||
match res {
|
||||
Ok(client) => {
|
||||
let boxed = Box::new(RemoteServerAdapterHandle(client));
|
||||
let boxed = Box::new(RemoteServerHandle(client));
|
||||
unsafe { *handle = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new RemoteServerClient from a handshake and adapter
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - An adapter created by this library
|
||||
/// * [`handshake`] - An RSD handshake from the same provider
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `provider` must be a valid pointer to a handle allocated by this library
|
||||
/// `handshake` must be a valid pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn remote_server_connect_rsd(
|
||||
provider: *mut AdapterHandle,
|
||||
handshake: *mut RsdHandshakeHandle,
|
||||
handle: *mut *mut RemoteServerHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || handshake.is_null() || handshake.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
let res: Result<RemoteServerClient<AdapterStream>, IdeviceError> =
|
||||
RUNTIME.block_on(async move {
|
||||
let provider_ref = unsafe { &mut (*provider).0 };
|
||||
let handshake_ref = unsafe { &mut (*handshake).0 };
|
||||
|
||||
// Connect using the reference
|
||||
RemoteServerClient::connect_rsd(provider_ref, handshake_ref).await
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(d) => {
|
||||
let boxed = Box::new(RemoteServerHandle(RemoteServerClient::new(Box::new(
|
||||
d.into_inner(),
|
||||
))));
|
||||
unsafe { *handle = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
@@ -56,35 +104,8 @@ pub unsafe extern "C" fn remote_server_adapter_new(
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library or NULL
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn remote_server_free(handle: *mut RemoteServerAdapterHandle) {
|
||||
pub unsafe extern "C" fn remote_server_free(handle: *mut RemoteServerHandle) {
|
||||
if !handle.is_null() {
|
||||
let _ = unsafe { Box::from_raw(handle) };
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying connection from a RemoteServerClient
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - The handle to get the connection from
|
||||
/// * [`connection`] - The newly allocated ConnectionHandle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library or NULL, and never used again
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn remote_server_adapter_into_inner(
|
||||
handle: *mut RemoteServerAdapterHandle,
|
||||
connection: *mut *mut AdapterHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() || connection.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let server = unsafe { Box::from_raw(handle) };
|
||||
let connection_obj = server.0.into_inner();
|
||||
let boxed = Box::new(AdapterHandle(connection_obj));
|
||||
unsafe { *connection = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
@@ -1,288 +0,0 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use std::ffi::{CStr, CString, c_char};
|
||||
|
||||
use idevice::{tcp::adapter::Adapter, xpc::XPCDevice};
|
||||
|
||||
use crate::{IdeviceErrorCode, RUNTIME, core_device_proxy::AdapterHandle};
|
||||
|
||||
/// Opaque handle to an XPCDevice
|
||||
pub struct XPCDeviceAdapterHandle(pub XPCDevice<Adapter>);
|
||||
|
||||
/// Opaque handle to an XPCService
|
||||
#[repr(C)]
|
||||
pub struct XPCServiceHandle {
|
||||
pub entitlement: *mut c_char,
|
||||
pub port: u16,
|
||||
pub uses_remote_xpc: bool,
|
||||
pub features: *mut *mut c_char,
|
||||
pub features_count: usize,
|
||||
pub service_version: i64,
|
||||
}
|
||||
|
||||
impl XPCServiceHandle {
|
||||
pub fn new(
|
||||
entitlement: *mut c_char,
|
||||
port: u16,
|
||||
uses_remote_xpc: bool,
|
||||
features: *mut *mut c_char,
|
||||
features_count: usize,
|
||||
service_version: i64,
|
||||
) -> Self {
|
||||
Self {
|
||||
entitlement,
|
||||
port,
|
||||
uses_remote_xpc,
|
||||
features,
|
||||
features_count,
|
||||
service_version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new XPCDevice from an adapter
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`adapter`] - The adapter to use for communication
|
||||
/// * [`device`] - Pointer to store the newly created XPCDevice handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `adapter` must be a valid pointer to a handle allocated by this library
|
||||
/// `device` must be a valid pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn xpc_device_new(
|
||||
adapter: *mut AdapterHandle,
|
||||
device: *mut *mut XPCDeviceAdapterHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if adapter.is_null() || device.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let adapter = unsafe { Box::from_raw(adapter) };
|
||||
let res = RUNTIME.block_on(async move { XPCDevice::new(adapter.0).await });
|
||||
|
||||
match res {
|
||||
// we have to unwrap res to avoid just getting a reference
|
||||
Ok(_) => {
|
||||
let boxed = Box::new(XPCDeviceAdapterHandle(res.unwrap()));
|
||||
unsafe { *device = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees an XPCDevice handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - The handle to free
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library or NULL
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn xpc_device_free(handle: *mut XPCDeviceAdapterHandle) {
|
||||
if !handle.is_null() {
|
||||
let _ = unsafe { Box::from_raw(handle) };
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a service by name from the XPCDevice
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - The XPCDevice handle
|
||||
/// * [`service_name`] - The name of the service to get
|
||||
/// * [`service`] - Pointer to store the newly created XPCService handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library
|
||||
/// `service_name` must be a valid null-terminated C string
|
||||
/// `service` must be a valid pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn xpc_device_get_service(
|
||||
handle: *mut XPCDeviceAdapterHandle,
|
||||
service_name: *const c_char,
|
||||
service: *mut *mut XPCServiceHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() || service_name.is_null() || service.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let device = unsafe { &(*handle).0 };
|
||||
let service_name_cstr = unsafe { CStr::from_ptr(service_name) };
|
||||
let service_name = match service_name_cstr.to_str() {
|
||||
Ok(s) => s,
|
||||
Err(_) => return IdeviceErrorCode::InvalidArg,
|
||||
};
|
||||
|
||||
let xpc_service = match device.services.get(service_name) {
|
||||
Some(s) => s,
|
||||
None => return IdeviceErrorCode::ServiceNotFound,
|
||||
};
|
||||
|
||||
// Convert features to C array if they exist
|
||||
let (features_ptr, features_count) = if let Some(features) = &xpc_service.features {
|
||||
let mut features_vec: Vec<*mut c_char> = features
|
||||
.iter()
|
||||
.map(|f| CString::new(f.as_str()).unwrap().into_raw())
|
||||
.collect();
|
||||
features_vec.shrink_to_fit();
|
||||
|
||||
let mut features_vec = Box::new(features_vec);
|
||||
let features_ptr = features_vec.as_mut_ptr();
|
||||
let features_len = features_vec.len();
|
||||
|
||||
Box::leak(features_vec);
|
||||
(features_ptr, features_len)
|
||||
} else {
|
||||
(std::ptr::null_mut(), 0)
|
||||
};
|
||||
|
||||
let boxed = Box::new(XPCServiceHandle {
|
||||
entitlement: CString::new(xpc_service.entitlement.as_str())
|
||||
.unwrap()
|
||||
.into_raw(),
|
||||
port: xpc_service.port,
|
||||
uses_remote_xpc: xpc_service.uses_remote_xpc,
|
||||
features: features_ptr,
|
||||
features_count,
|
||||
service_version: xpc_service.service_version.unwrap_or(-1),
|
||||
});
|
||||
|
||||
unsafe { *service = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
/// Returns the adapter in the RemoteXPC Device
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - The handle to get the adapter from
|
||||
/// * [`adapter`] - The newly allocated AdapterHandle
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library or NULL, and never used again
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn xpc_device_adapter_into_inner(
|
||||
handle: *mut XPCDeviceAdapterHandle,
|
||||
adapter: *mut *mut AdapterHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
let device = unsafe { Box::from_raw(handle).0 };
|
||||
let adapter_obj = device.into_inner();
|
||||
let boxed = Box::new(AdapterHandle(adapter_obj));
|
||||
unsafe { *adapter = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
/// Frees an XPCService handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - The handle to free
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library or NULL
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn xpc_service_free(handle: *mut XPCServiceHandle) {
|
||||
if !handle.is_null() {
|
||||
let handle = unsafe { Box::from_raw(handle) };
|
||||
|
||||
// Free the entitlement string
|
||||
if !handle.entitlement.is_null() {
|
||||
let _ = unsafe { CString::from_raw(handle.entitlement) };
|
||||
}
|
||||
|
||||
// Free the features array
|
||||
if !handle.features.is_null() {
|
||||
for i in 0..handle.features_count {
|
||||
let feature_ptr = unsafe { *handle.features.add(i) };
|
||||
if !feature_ptr.is_null() {
|
||||
let _ = unsafe { CString::from_raw(feature_ptr) };
|
||||
}
|
||||
}
|
||||
let _ = unsafe {
|
||||
Vec::from_raw_parts(
|
||||
handle.features,
|
||||
handle.features_count,
|
||||
handle.features_count,
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the list of available service names
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - The XPCDevice handle
|
||||
/// * [`names`] - Pointer to store the array of service names
|
||||
/// * [`count`] - Pointer to store the number of services
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library
|
||||
/// `names` must be a valid pointer to a location where the array will be stored
|
||||
/// `count` must be a valid pointer to a location where the count will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn xpc_device_get_service_names(
|
||||
handle: *mut XPCDeviceAdapterHandle,
|
||||
names: *mut *mut *mut c_char,
|
||||
count: *mut usize,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() || names.is_null() || count.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let device = unsafe { &(*handle).0 };
|
||||
let service_names: Vec<CString> = device
|
||||
.services
|
||||
.keys()
|
||||
.map(|k| CString::new(k.as_str()).unwrap())
|
||||
.collect();
|
||||
|
||||
let mut name_ptrs: Vec<*mut c_char> = service_names.into_iter().map(|s| s.into_raw()).collect();
|
||||
|
||||
if name_ptrs.is_empty() {
|
||||
unsafe {
|
||||
*names = std::ptr::null_mut();
|
||||
*count = 0;
|
||||
}
|
||||
} else {
|
||||
name_ptrs.shrink_to_fit();
|
||||
unsafe {
|
||||
*names = name_ptrs.as_mut_ptr();
|
||||
*count = name_ptrs.len();
|
||||
}
|
||||
std::mem::forget(name_ptrs);
|
||||
}
|
||||
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
/// Frees a list of service names
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`names`] - The array of service names to free
|
||||
/// * [`count`] - The number of services in the array
|
||||
///
|
||||
/// # Safety
|
||||
/// `names` must be a valid pointer to an array of `count` C strings
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn xpc_device_free_service_names(names: *mut *mut c_char, count: usize) {
|
||||
if !names.is_null() && count > 0 {
|
||||
let names_vec = unsafe { Vec::from_raw_parts(names, count, count) };
|
||||
for name in names_vec {
|
||||
if !name.is_null() {
|
||||
let _ = unsafe { CString::from_raw(name) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
508
ffi/src/rsd.rs
Normal file
508
ffi/src/rsd.rs
Normal file
@@ -0,0 +1,508 @@
|
||||
//! Remote Service Discovery (RSD) Client Bindings
|
||||
//!
|
||||
//! Provides C-compatible bindings for RSD handshake and service discovery on iOS devices.
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ptr;
|
||||
|
||||
use idevice::rsd::RsdHandshake;
|
||||
|
||||
use crate::{IdeviceErrorCode, RUNTIME, ReadWriteOpaque};
|
||||
|
||||
/// Opaque handle to an RsdHandshake
|
||||
pub struct RsdHandshakeHandle(pub RsdHandshake);
|
||||
|
||||
/// C-compatible representation of an RSD service
|
||||
#[repr(C)]
|
||||
pub struct CRsdService {
|
||||
/// Service name (null-terminated string)
|
||||
pub name: *mut libc::c_char,
|
||||
/// Required entitlement (null-terminated string)
|
||||
pub entitlement: *mut libc::c_char,
|
||||
/// Port number
|
||||
pub port: u16,
|
||||
/// Whether service uses remote XPC
|
||||
pub uses_remote_xpc: bool,
|
||||
/// Number of features
|
||||
pub features_count: libc::size_t,
|
||||
/// Array of feature strings
|
||||
pub features: *mut *mut libc::c_char,
|
||||
/// Service version (-1 if not present)
|
||||
pub service_version: i64,
|
||||
}
|
||||
|
||||
/// Array of RSD services returned by rsd_get_services
|
||||
#[repr(C)]
|
||||
pub struct CRsdServiceArray {
|
||||
/// Array of services
|
||||
pub services: *mut CRsdService,
|
||||
/// Number of services in array
|
||||
pub count: libc::size_t,
|
||||
}
|
||||
|
||||
/// Creates a new RSD handshake from a ReadWrite connection
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`socket`] - The connection to use for communication
|
||||
/// * [`handle`] - Pointer to store the newly created RsdHandshake handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `socket` must be a valid pointer to a ReadWrite handle allocated by this library. It is
|
||||
/// consumed and may not be used again.
|
||||
/// `handle` must be a valid pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rsd_handshake_new(
|
||||
socket: *mut ReadWriteOpaque,
|
||||
handle: *mut *mut RsdHandshakeHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if socket.is_null() || handle.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let wrapper = unsafe { &mut *socket };
|
||||
|
||||
let res = match wrapper.inner.take() {
|
||||
Some(mut w) => RUNTIME.block_on(async move { RsdHandshake::new(w.as_mut()).await }),
|
||||
None => {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
};
|
||||
|
||||
match res {
|
||||
Ok(handshake) => {
|
||||
let boxed = Box::new(RsdHandshakeHandle(handshake));
|
||||
unsafe { *handle = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the protocol version from the RSD handshake
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - A valid RsdHandshake handle
|
||||
/// * [`version`] - Pointer to store the protocol version
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library
|
||||
/// `version` must be a valid pointer to store the version
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rsd_get_protocol_version(
|
||||
handle: *mut RsdHandshakeHandle,
|
||||
version: *mut libc::size_t,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() || version.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
*version = (*handle).0.protocol_version;
|
||||
}
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
/// Gets the UUID from the RSD handshake
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - A valid RsdHandshake handle
|
||||
/// * [`uuid`] - Pointer to store the UUID string (caller must free with rsd_free_string)
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library
|
||||
/// `uuid` must be a valid pointer to store the string pointer
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rsd_get_uuid(
|
||||
handle: *mut RsdHandshakeHandle,
|
||||
uuid: *mut *mut libc::c_char,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() || uuid.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let uuid_str = &unsafe { &*handle }.0.uuid;
|
||||
match CString::new(uuid_str.as_str()) {
|
||||
Ok(c_str) => {
|
||||
unsafe { *uuid = c_str.into_raw() };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(_) => IdeviceErrorCode::InvalidString,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets all available services from the RSD handshake
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - A valid RsdHandshake handle
|
||||
/// * [`services`] - Pointer to store the services array
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library
|
||||
/// `services` must be a valid pointer to store the services array
|
||||
/// Caller must free the returned array with rsd_free_services
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rsd_get_services(
|
||||
handle: *mut RsdHandshakeHandle,
|
||||
services: *mut *mut CRsdServiceArray,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() || services.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let handshake = unsafe { &*handle };
|
||||
let service_map = &handshake.0.services;
|
||||
|
||||
let count = service_map.len();
|
||||
let mut c_services = Vec::with_capacity(count);
|
||||
|
||||
for (name, service) in service_map.iter() {
|
||||
// Convert name
|
||||
let c_name = match CString::new(name.as_str()) {
|
||||
Ok(s) => s.into_raw(),
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
// Convert entitlement
|
||||
let c_entitlement = match CString::new(service.entitlement.as_str()) {
|
||||
Ok(s) => s.into_raw(),
|
||||
Err(_) => {
|
||||
unsafe {
|
||||
let _ = CString::from_raw(c_name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Convert features
|
||||
let (features_ptr, features_count) = match &service.features {
|
||||
Some(features) => {
|
||||
let mut c_features = Vec::with_capacity(features.len());
|
||||
|
||||
for feature in features {
|
||||
match CString::new(feature.as_str()) {
|
||||
Ok(s) => c_features.push(s.into_raw()),
|
||||
Err(_) => {
|
||||
// Clean up already allocated features
|
||||
for f in c_features {
|
||||
unsafe {
|
||||
let _ = CString::from_raw(f);
|
||||
}
|
||||
}
|
||||
// Return early to avoid the move below
|
||||
return IdeviceErrorCode::InvalidString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All features converted successfully
|
||||
let boxed = c_features.into_boxed_slice();
|
||||
let ptr = Box::into_raw(boxed) as *mut *mut libc::c_char;
|
||||
(ptr, features.len())
|
||||
}
|
||||
None => (ptr::null_mut(), 0),
|
||||
};
|
||||
|
||||
let c_service = CRsdService {
|
||||
name: c_name,
|
||||
entitlement: c_entitlement,
|
||||
port: service.port,
|
||||
uses_remote_xpc: service.uses_remote_xpc,
|
||||
features_count,
|
||||
features: features_ptr,
|
||||
service_version: service.service_version.unwrap_or(-1),
|
||||
};
|
||||
|
||||
c_services.push(c_service);
|
||||
}
|
||||
|
||||
let boxed_services = c_services.into_boxed_slice();
|
||||
let array = Box::new(CRsdServiceArray {
|
||||
services: Box::into_raw(boxed_services) as *mut CRsdService,
|
||||
count,
|
||||
});
|
||||
|
||||
unsafe { *services = Box::into_raw(array) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
/// Checks if a specific service is available
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - A valid RsdHandshake handle
|
||||
/// * [`service_name`] - Name of the service to check for
|
||||
/// * [`available`] - Pointer to store the availability result
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library
|
||||
/// `service_name` must be a valid C string
|
||||
/// `available` must be a valid pointer to store the boolean result
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rsd_service_available(
|
||||
handle: *mut RsdHandshakeHandle,
|
||||
service_name: *const libc::c_char,
|
||||
available: *mut bool,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() || service_name.is_null() || available.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let name = match unsafe { CStr::from_ptr(service_name) }.to_str() {
|
||||
Ok(s) => s,
|
||||
Err(_) => return IdeviceErrorCode::InvalidArg,
|
||||
};
|
||||
|
||||
let handshake = unsafe { &*handle };
|
||||
unsafe { *available = handshake.0.services.contains_key(name) };
|
||||
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
/// Gets information about a specific service
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - A valid RsdHandshake handle
|
||||
/// * [`service_name`] - Name of the service to get info for
|
||||
/// * [`service_info`] - Pointer to store the service information
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library
|
||||
/// `service_name` must be a valid C string
|
||||
/// `service_info` must be a valid pointer to store the service info
|
||||
/// Caller must free the returned service with rsd_free_service
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rsd_get_service_info(
|
||||
handle: *mut RsdHandshakeHandle,
|
||||
service_name: *const libc::c_char,
|
||||
service_info: *mut *mut CRsdService,
|
||||
) -> IdeviceErrorCode {
|
||||
if handle.is_null() || service_name.is_null() || service_info.is_null() {
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let name = match unsafe { CStr::from_ptr(service_name) }.to_str() {
|
||||
Ok(s) => s,
|
||||
Err(_) => return IdeviceErrorCode::InvalidArg,
|
||||
};
|
||||
|
||||
let handshake = unsafe { &*handle };
|
||||
let service = match handshake.0.services.get(name) {
|
||||
Some(s) => s,
|
||||
None => return IdeviceErrorCode::ServiceNotFound,
|
||||
};
|
||||
|
||||
// Convert service to C representation (similar to rsd_get_services logic)
|
||||
let c_name = match CString::new(name) {
|
||||
Ok(s) => s.into_raw(),
|
||||
Err(_) => return IdeviceErrorCode::InvalidString,
|
||||
};
|
||||
|
||||
let c_entitlement = match CString::new(service.entitlement.as_str()) {
|
||||
Ok(s) => s.into_raw(),
|
||||
Err(_) => {
|
||||
unsafe {
|
||||
let _ = CString::from_raw(c_name);
|
||||
}
|
||||
return IdeviceErrorCode::InvalidString;
|
||||
}
|
||||
};
|
||||
|
||||
// Convert features
|
||||
let (features_ptr, features_count) = match &service.features {
|
||||
Some(features) => {
|
||||
let mut c_features = Vec::with_capacity(features.len());
|
||||
for feature in features {
|
||||
match CString::new(feature.as_str()) {
|
||||
Ok(s) => c_features.push(s.into_raw()),
|
||||
Err(_) => {
|
||||
// Clean up already allocated features
|
||||
for f in c_features {
|
||||
unsafe {
|
||||
let _ = CString::from_raw(f);
|
||||
}
|
||||
}
|
||||
// Clean up name and entitlement
|
||||
unsafe {
|
||||
let _ = CString::from_raw(c_name);
|
||||
let _ = CString::from_raw(c_entitlement);
|
||||
}
|
||||
return IdeviceErrorCode::InvalidString;
|
||||
}
|
||||
}
|
||||
}
|
||||
let boxed = c_features.into_boxed_slice();
|
||||
(
|
||||
Box::into_raw(boxed) as *mut *mut libc::c_char,
|
||||
features.len(),
|
||||
)
|
||||
}
|
||||
None => (ptr::null_mut(), 0),
|
||||
};
|
||||
|
||||
let c_service = Box::new(CRsdService {
|
||||
name: c_name,
|
||||
entitlement: c_entitlement,
|
||||
port: service.port,
|
||||
uses_remote_xpc: service.uses_remote_xpc,
|
||||
features_count,
|
||||
features: features_ptr,
|
||||
service_version: service.service_version.unwrap_or(-1),
|
||||
});
|
||||
|
||||
unsafe { *service_info = Box::into_raw(c_service) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
/// Frees a string returned by RSD functions
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`string`] - The string to free
|
||||
///
|
||||
/// # Safety
|
||||
/// Must only be called with strings returned from RSD functions
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rsd_free_string(string: *mut libc::c_char) {
|
||||
if !string.is_null() {
|
||||
unsafe {
|
||||
let _ = CString::from_raw(string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees a single service returned by rsd_get_service_info
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`service`] - The service to free
|
||||
///
|
||||
/// # Safety
|
||||
/// Must only be called with services returned from rsd_get_service_info
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rsd_free_service(service: *mut CRsdService) {
|
||||
if service.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let service_box = unsafe { Box::from_raw(service) };
|
||||
|
||||
// Free name
|
||||
if !service_box.name.is_null() {
|
||||
unsafe {
|
||||
let _ = CString::from_raw(service_box.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Free entitlement
|
||||
if !service_box.entitlement.is_null() {
|
||||
unsafe {
|
||||
let _ = CString::from_raw(service_box.entitlement);
|
||||
}
|
||||
}
|
||||
|
||||
// Free features array
|
||||
if !service_box.features.is_null() && service_box.features_count > 0 {
|
||||
let features_slice = unsafe {
|
||||
std::slice::from_raw_parts_mut(service_box.features, service_box.features_count)
|
||||
};
|
||||
for feature_ptr in features_slice.iter() {
|
||||
if !feature_ptr.is_null() {
|
||||
unsafe {
|
||||
let _ = CString::from_raw(*feature_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
let _ = Box::from_raw(features_slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees services array returned by rsd_get_services
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`services`] - The services array to free
|
||||
///
|
||||
/// # Safety
|
||||
/// Must only be called with arrays returned from rsd_get_services
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rsd_free_services(services: *mut CRsdServiceArray) {
|
||||
if services.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let services_box = unsafe { Box::from_raw(services) };
|
||||
|
||||
if !services_box.services.is_null() && services_box.count > 0 {
|
||||
let services_slice =
|
||||
unsafe { std::slice::from_raw_parts_mut(services_box.services, services_box.count) };
|
||||
|
||||
// Free each service
|
||||
for service in services_slice.iter() {
|
||||
// Free name
|
||||
if !service.name.is_null() {
|
||||
unsafe {
|
||||
let _ = CString::from_raw(service.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Free entitlement
|
||||
if !service.entitlement.is_null() {
|
||||
unsafe {
|
||||
let _ = CString::from_raw(service.entitlement);
|
||||
}
|
||||
}
|
||||
|
||||
// Free features array
|
||||
if !service.features.is_null() && service.features_count > 0 {
|
||||
let features_slice = unsafe {
|
||||
std::slice::from_raw_parts_mut(service.features, service.features_count)
|
||||
};
|
||||
for feature_ptr in features_slice.iter() {
|
||||
if !feature_ptr.is_null() {
|
||||
unsafe {
|
||||
let _ = CString::from_raw(*feature_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
let _ = Box::from_raw(features_slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _ = Box::from_raw(services_slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees an RSD handshake handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`handle`] - The handle to free
|
||||
///
|
||||
/// # Safety
|
||||
/// `handle` must be a valid pointer to a handle allocated by this library,
|
||||
/// or NULL (in which case this function does nothing)
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn rsd_handshake_free(handle: *mut RsdHandshakeHandle) {
|
||||
if !handle.is_null() {
|
||||
let _ = unsafe { Box::from_raw(handle) };
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
use std::ffi::{CStr, c_void};
|
||||
|
||||
use idevice::{IdeviceError, IdeviceService, springboardservices::SpringBoardServicesClient};
|
||||
|
||||
use crate::{
|
||||
IdeviceErrorCode, IdeviceHandle, RUNTIME,
|
||||
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
|
||||
use idevice::{
|
||||
IdeviceError, IdeviceService, provider::IdeviceProvider,
|
||||
springboardservices::SpringBoardServicesClient,
|
||||
};
|
||||
|
||||
use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle};
|
||||
|
||||
pub struct SpringBoardServicesClientHandle(pub SpringBoardServicesClient);
|
||||
|
||||
/// Connects to the Springboard service using a TCP provider
|
||||
/// Connects to the Springboard service using a provider
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A TcpProvider
|
||||
/// * [`provider`] - An IdeviceProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated SpringBoardServicesClient handle
|
||||
///
|
||||
/// # Returns
|
||||
@@ -22,8 +22,8 @@ pub struct SpringBoardServicesClientHandle(pub SpringBoardServicesClient);
|
||||
/// `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 springboard_services_connect_tcp(
|
||||
provider: *mut TcpProviderHandle,
|
||||
pub unsafe extern "C" fn springboard_services_connect(
|
||||
provider: *mut IdeviceProviderHandle,
|
||||
client: *mut *mut SpringBoardServicesClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() || client.is_null() {
|
||||
@@ -32,18 +32,8 @@ pub unsafe extern "C" fn springboard_services_connect_tcp(
|
||||
}
|
||||
|
||||
let res: Result<SpringBoardServicesClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the provider (without immediately dropping it)
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let provider_ref = &provider_box.0;
|
||||
|
||||
// Connect using the reference
|
||||
let result = SpringBoardServicesClient::connect(provider_ref).await;
|
||||
|
||||
// Explicitly keep the provider_box alive until after connect completes
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||
SpringBoardServicesClient::connect(provider_ref).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -61,53 +51,6 @@ pub unsafe extern "C" fn springboard_services_connect_tcp(
|
||||
}
|
||||
}
|
||||
|
||||
/// Connects to the Springboard service using a usbmuxd provider
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A UsbmuxdProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated SpringBoardServicesClient handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # 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 springboard_services_connect_usbmuxd(
|
||||
provider: *mut UsbmuxdProviderHandle,
|
||||
client: *mut *mut SpringBoardServicesClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() {
|
||||
log::error!("Provider is null");
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res: Result<SpringBoardServicesClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
// Take ownership of the provider (without immediately dropping it)
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
|
||||
// Get a reference to the inner value
|
||||
let provider_ref = &provider_box.0;
|
||||
|
||||
// Connect using the reference
|
||||
let result = SpringBoardServicesClient::connect(provider_ref).await;
|
||||
|
||||
// Explicitly keep the provider_box alive until after connect completes
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
let boxed = Box::new(SpringBoardServicesClientHandle(r));
|
||||
unsafe { *client = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new SpringBoardServices client from an existing Idevice connection
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -118,7 +61,8 @@ pub unsafe extern "C" fn springboard_services_connect_usbmuxd(
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `socket` must be a valid pointer to a handle allocated by this library
|
||||
/// `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 springboard_services_new(
|
||||
@@ -1,26 +1,26 @@
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use idevice::{syslog_relay::SyslogRelayClient, IdeviceError, IdeviceService};
|
||||
|
||||
use crate::{
|
||||
provider::TcpProviderHandle, IdeviceErrorCode, RUNTIME
|
||||
use idevice::{
|
||||
IdeviceError, IdeviceService, provider::IdeviceProvider, syslog_relay::SyslogRelayClient,
|
||||
};
|
||||
|
||||
use crate::{IdeviceErrorCode, RUNTIME, provider::IdeviceProviderHandle};
|
||||
|
||||
pub struct SyslogRelayClientHandle(pub SyslogRelayClient);
|
||||
|
||||
/// Automatically creates and connects to syslog relay, returning a client handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - A TcpProvider
|
||||
/// * [`provider`] - An IdeviceProvider
|
||||
/// * [`client`] - On success, will be set to point to a newly allocated SyslogRelayClient handle
|
||||
///
|
||||
/// # 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 extern "C" fn syslog_relay_connect_tcp(
|
||||
provider: *mut TcpProviderHandle,
|
||||
client: *mut *mut SyslogRelayClientHandle
|
||||
pub unsafe extern "C" fn syslog_relay_connect_tcp(
|
||||
provider: *mut IdeviceProviderHandle,
|
||||
client: *mut *mut SyslogRelayClientHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
if provider.is_null() {
|
||||
log::error!("Null pointer provided");
|
||||
@@ -28,14 +28,8 @@ pub extern "C" fn syslog_relay_connect_tcp(
|
||||
}
|
||||
|
||||
let res: Result<SyslogRelayClient, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let provider_box = unsafe { Box::from_raw(provider) };
|
||||
|
||||
let provider_ref = &provider_box.0;
|
||||
|
||||
let result = SyslogRelayClient::connect(provider_ref).await;
|
||||
|
||||
std::mem::forget(provider_box);
|
||||
result
|
||||
let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
|
||||
SyslogRelayClient::connect(provider_ref).await
|
||||
});
|
||||
|
||||
match res {
|
||||
@@ -62,9 +56,7 @@ pub extern "C" fn syslog_relay_connect_tcp(
|
||||
/// `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 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() {
|
||||
log::debug!("Freeing syslog relay client");
|
||||
let _ = unsafe { Box::from_raw(handle) };
|
||||
@@ -81,7 +73,7 @@ pub extern "C" fn syslog_relay_client_free(
|
||||
/// `client` must be a valid pointer to a handle allocated by this library
|
||||
/// `log_message` must be a valid, non-null pointer to a location where the log message will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn syslog_relay_next(
|
||||
pub unsafe extern "C" fn syslog_relay_next(
|
||||
client: *mut SyslogRelayClientHandle,
|
||||
log_message: *mut *mut c_char,
|
||||
) -> IdeviceErrorCode {
|
||||
@@ -89,12 +81,7 @@ pub extern "C" fn syslog_relay_next(
|
||||
return IdeviceErrorCode::InvalidArg;
|
||||
}
|
||||
|
||||
let res = RUNTIME.block_on(async {
|
||||
unsafe { &mut *client }
|
||||
.0
|
||||
.next()
|
||||
.await
|
||||
});
|
||||
let res = RUNTIME.block_on(async { unsafe { &mut *client }.0.next().await });
|
||||
|
||||
match res {
|
||||
Ok(log) => {
|
||||
|
||||
@@ -26,7 +26,6 @@ base64 = { version = "0.22" }
|
||||
|
||||
indexmap = { version = "2.7", features = ["serde"], optional = true }
|
||||
uuid = { version = "1.12", features = ["serde", "v4"], optional = true }
|
||||
async-recursion = { version = "1.1", optional = true }
|
||||
chrono = { version = "0.4.40", optional = true, default-features = false }
|
||||
|
||||
serde_json = { version = "1", optional = true }
|
||||
@@ -50,7 +49,7 @@ x509-cert = { version = "0.2", optional = true, features = [
|
||||
], default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.43", features = ["fs"] }
|
||||
tokio = { version = "1.43", features = ["full"] }
|
||||
tun-rs = { version = "2.0.8", features = ["async_tokio"] }
|
||||
bytes = "1.10.1"
|
||||
|
||||
@@ -69,19 +68,14 @@ misagent = []
|
||||
mobile_image_mounter = ["dep:sha2"]
|
||||
location_simulation = []
|
||||
pair = ["chrono/default", "tokio/time", "dep:sha2", "dep:rsa", "dep:x509-cert"]
|
||||
rsd = ["xpc"]
|
||||
syslog_relay = ["dep:bytes"]
|
||||
tcp = ["tokio/net"]
|
||||
tunnel_tcp_stack = ["dep:rand", "dep:futures", "tokio/fs", "tokio/sync"]
|
||||
tss = ["dep:uuid", "dep:reqwest"]
|
||||
tunneld = ["dep:serde_json", "dep:json", "dep:reqwest"]
|
||||
usbmuxd = ["tokio/net"]
|
||||
xpc = [
|
||||
"tokio/sync",
|
||||
"dep:indexmap",
|
||||
"dep:uuid",
|
||||
"dep:async-recursion",
|
||||
"dep:json",
|
||||
]
|
||||
xpc = ["dep:indexmap", "dep:uuid"]
|
||||
full = [
|
||||
"afc",
|
||||
"amfi",
|
||||
@@ -98,6 +92,7 @@ full = [
|
||||
"usbmuxd",
|
||||
"xpc",
|
||||
"location_simulation",
|
||||
"rsd",
|
||||
"tcp",
|
||||
"tunnel_tcp_stack",
|
||||
"tss",
|
||||
|
||||
@@ -15,12 +15,17 @@ pub mod tunneld;
|
||||
#[cfg(feature = "usbmuxd")]
|
||||
pub mod usbmuxd;
|
||||
mod util;
|
||||
#[cfg(feature = "xpc")]
|
||||
pub mod xpc;
|
||||
|
||||
pub mod services;
|
||||
pub use services::*;
|
||||
|
||||
#[cfg(feature = "xpc")]
|
||||
pub use xpc::RemoteXpcClient;
|
||||
|
||||
use log::{debug, error, trace};
|
||||
use provider::IdeviceProvider;
|
||||
use provider::{IdeviceProvider, RsdProvider};
|
||||
use rustls::{crypto::CryptoProvider, pki_types::ServerName};
|
||||
use std::{
|
||||
io::{self, BufWriter},
|
||||
@@ -60,6 +65,25 @@ pub trait IdeviceService: Sized {
|
||||
) -> impl std::future::Future<Output = Result<Self, IdeviceError>> + Send;
|
||||
}
|
||||
|
||||
pub trait RsdService: Sized {
|
||||
fn rsd_service_name() -> &'static str;
|
||||
fn from_stream(
|
||||
stream: Self::Stream,
|
||||
) -> impl std::future::Future<Output = Result<Self, IdeviceError>> + Send;
|
||||
fn connect_rsd<'a, S>(
|
||||
provider: &'a mut impl RsdProvider<'a, Stream = S>,
|
||||
handshake: &mut rsd::RsdHandshake,
|
||||
) -> impl std::future::Future<Output = Result<Self, IdeviceError>>
|
||||
where
|
||||
Self: crate::RsdService<Stream = S>,
|
||||
S: ReadWrite,
|
||||
{
|
||||
handshake.connect(provider)
|
||||
}
|
||||
|
||||
type Stream: ReadWrite;
|
||||
}
|
||||
|
||||
/// Type alias for boxed device connection sockets
|
||||
///
|
||||
/// Used to enable dynamic dispatch of different connection types while maintaining
|
||||
@@ -419,6 +443,8 @@ pub enum IdeviceError {
|
||||
HeartbeatTimeout,
|
||||
#[error("not found")]
|
||||
NotFound,
|
||||
#[error("service not found")]
|
||||
ServiceNotFound,
|
||||
#[error("CDTunnel packet too short")]
|
||||
CdtunnelPacketTooShort,
|
||||
#[error("CDTunnel packet invalid magic")]
|
||||
@@ -498,8 +524,44 @@ pub enum IdeviceError {
|
||||
InternalError(String),
|
||||
|
||||
#[cfg(feature = "xpc")]
|
||||
#[error("xpc message failed")]
|
||||
Xpc(#[from] xpc::error::XPCError),
|
||||
#[error("unknown http frame type")]
|
||||
UnknownFrame(u8),
|
||||
|
||||
#[cfg(feature = "xpc")]
|
||||
#[error("unknown http setting type")]
|
||||
UnknownHttpSetting(u16),
|
||||
|
||||
#[cfg(feature = "xpc")]
|
||||
#[error("Unintialized stream ID")]
|
||||
UninitializedStreamId,
|
||||
|
||||
#[cfg(feature = "xpc")]
|
||||
#[error("unknown XPC type")]
|
||||
UnknownXpcType(u32),
|
||||
|
||||
#[cfg(feature = "xpc")]
|
||||
#[error("malformed XPC message")]
|
||||
MalformedXpc,
|
||||
|
||||
#[cfg(feature = "xpc")]
|
||||
#[error("invalid XPC magic")]
|
||||
InvalidXpcMagic,
|
||||
|
||||
#[cfg(feature = "xpc")]
|
||||
#[error("unexpected XPC version")]
|
||||
UnexpectedXpcVersion,
|
||||
|
||||
#[cfg(feature = "xpc")]
|
||||
#[error("invalid C string")]
|
||||
InvalidCString,
|
||||
|
||||
#[cfg(feature = "xpc")]
|
||||
#[error("stream reset")]
|
||||
HttpStreamReset,
|
||||
|
||||
#[cfg(feature = "xpc")]
|
||||
#[error("go away packet received")]
|
||||
HttpGoAway(String),
|
||||
|
||||
#[cfg(feature = "dvt")]
|
||||
#[error("NSKeyedArchive error")]
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::{future::Future, pin::Pin};
|
||||
#[cfg(feature = "tcp")]
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
use crate::{pairing_file::PairingFile, Idevice, IdeviceError};
|
||||
use crate::{pairing_file::PairingFile, Idevice, IdeviceError, ReadWrite};
|
||||
|
||||
#[cfg(feature = "usbmuxd")]
|
||||
use crate::usbmuxd::UsbmuxdAddr;
|
||||
@@ -42,6 +42,14 @@ pub trait IdeviceProvider: Unpin + Send + Sync + std::fmt::Debug {
|
||||
) -> Pin<Box<dyn Future<Output = Result<PairingFile, IdeviceError>> + Send>>;
|
||||
}
|
||||
|
||||
pub trait RsdProvider<'a>: Unpin + Send + Sync + std::fmt::Debug {
|
||||
fn connect_to_service_port(
|
||||
&'a mut self,
|
||||
port: u16,
|
||||
) -> impl std::future::Future<Output = Result<Self::Stream, IdeviceError>> + Send;
|
||||
type Stream: ReadWrite;
|
||||
}
|
||||
|
||||
/// TCP-based device connection provider
|
||||
#[cfg(feature = "tcp")]
|
||||
#[derive(Debug)]
|
||||
@@ -149,3 +157,15 @@ impl IdeviceProvider for UsbmuxdProvider {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
impl<'a> RsdProvider<'a> for std::net::IpAddr {
|
||||
async fn connect_to_service_port(
|
||||
&'a mut self,
|
||||
port: u16,
|
||||
) -> Result<Self::Stream, IdeviceError> {
|
||||
Ok(tokio::net::TcpStream::connect((*self, port)).await?)
|
||||
}
|
||||
|
||||
type Stream = tokio::net::TcpStream;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,22 @@ use log::debug;
|
||||
use std::fmt::Write;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
use crate::{IdeviceError, ReadWrite};
|
||||
use crate::{IdeviceError, ReadWrite, RsdService};
|
||||
|
||||
/// The service name for the debug proxy as registered with lockdownd
|
||||
pub const SERVICE_NAME: &str = "com.apple.internal.dt.remote.debugproxy";
|
||||
impl<R: ReadWrite> RsdService for DebugProxyClient<R> {
|
||||
fn rsd_service_name() -> &'static str {
|
||||
"com.apple.internal.dt.remote.debugproxy"
|
||||
}
|
||||
|
||||
async fn from_stream(stream: R) -> Result<Self, IdeviceError> {
|
||||
Ok(Self {
|
||||
socket: stream,
|
||||
noack_mode: false,
|
||||
})
|
||||
}
|
||||
|
||||
type Stream = R;
|
||||
}
|
||||
|
||||
/// Client for interacting with the iOS debug proxy service
|
||||
///
|
||||
@@ -102,6 +114,7 @@ impl<R: ReadWrite> DebugProxyClient<R> {
|
||||
|
||||
// Send the packet
|
||||
self.socket.write_all(packet.as_bytes()).await?;
|
||||
self.socket.flush().await?;
|
||||
|
||||
// Read the response
|
||||
let response = self.read_response().await?;
|
||||
@@ -161,6 +174,7 @@ impl<R: ReadWrite> DebugProxyClient<R> {
|
||||
/// Returns `IdeviceError` if writing fails
|
||||
pub async fn send_raw(&mut self, bytes: &[u8]) -> Result<(), IdeviceError> {
|
||||
self.socket.write_all(bytes).await?;
|
||||
self.socket.flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -242,6 +256,7 @@ impl<R: ReadWrite> DebugProxyClient<R> {
|
||||
/// Returns `IdeviceError` if writing fails
|
||||
pub async fn send_ack(&mut self) -> Result<(), IdeviceError> {
|
||||
self.socket.write_all(b"+").await?;
|
||||
self.socket.flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -251,6 +266,7 @@ impl<R: ReadWrite> DebugProxyClient<R> {
|
||||
/// Returns `IdeviceError` if writing fails
|
||||
pub async fn send_noack(&mut self) -> Result<(), IdeviceError> {
|
||||
self.socket.write_all(b"-").await?;
|
||||
self.socket.flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -300,4 +316,3 @@ impl From<&str> for DebugserverCommand {
|
||||
s.to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use crate::{IdeviceError, ReadWrite, RsdService};
|
||||
|
||||
#[cfg(feature = "location_simulation")]
|
||||
pub mod location_simulation;
|
||||
pub mod message;
|
||||
pub mod process_control;
|
||||
pub mod remote_server;
|
||||
|
||||
pub const SERVICE_NAME: &str = "com.apple.instruments.dtservicehub";
|
||||
impl<R: ReadWrite> RsdService for remote_server::RemoteServerClient<R> {
|
||||
fn rsd_service_name() -> &'static str {
|
||||
"com.apple.instruments.dtservicehub"
|
||||
}
|
||||
|
||||
async fn from_stream(stream: R) -> Result<Self, IdeviceError> {
|
||||
Ok(Self::new(stream))
|
||||
}
|
||||
|
||||
type Stream = R;
|
||||
}
|
||||
|
||||
@@ -202,6 +202,7 @@ impl<R: ReadWrite> RemoteServerClient<R> {
|
||||
let message = Message::new(mheader, pheader, aux, data);
|
||||
debug!("Sending message: {message:#?}");
|
||||
self.idevice.write_all(&message.serialize()).await?;
|
||||
self.idevice.flush().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ pub mod misagent;
|
||||
pub mod mobile_image_mounter;
|
||||
#[cfg(feature = "syslog_relay")]
|
||||
pub mod os_trace_relay;
|
||||
#[cfg(feature = "rsd")]
|
||||
pub mod rsd;
|
||||
#[cfg(feature = "springboardservices")]
|
||||
pub mod springboardservices;
|
||||
#[cfg(feature = "syslog_relay")]
|
||||
pub mod syslog_relay;
|
||||
#[cfg(feature = "xpc")]
|
||||
pub mod xpc;
|
||||
|
||||
177
idevice/src/services/rsd.rs
Normal file
177
idevice/src/services/rsd.rs
Normal file
@@ -0,0 +1,177 @@
|
||||
//! Remote Service Discovery
|
||||
//! Communicates via XPC and returns advertised services
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use log::warn;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{provider::RsdProvider, IdeviceError, ReadWrite, RemoteXpcClient};
|
||||
|
||||
/// Describes an available XPC service
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct RsdService {
|
||||
/// Required entitlement to access this service
|
||||
pub entitlement: String,
|
||||
/// Port number where the service is available
|
||||
pub port: u16,
|
||||
/// Whether the service uses remote XPC
|
||||
pub uses_remote_xpc: bool,
|
||||
/// Optional list of supported features
|
||||
pub features: Option<Vec<String>>,
|
||||
/// Optional service version number
|
||||
pub service_version: Option<i64>,
|
||||
}
|
||||
|
||||
pub struct RsdHandshake {
|
||||
pub services: HashMap<String, RsdService>,
|
||||
pub protocol_version: usize,
|
||||
pub properties: HashMap<String, plist::Value>,
|
||||
pub uuid: String,
|
||||
}
|
||||
|
||||
impl RsdHandshake {
|
||||
pub async fn new(socket: impl ReadWrite) -> Result<Self, IdeviceError> {
|
||||
let mut xpc_client = RemoteXpcClient::new(socket).await?;
|
||||
let data = xpc_client.do_handshake().await?;
|
||||
|
||||
let services_dict = match data
|
||||
.as_dictionary()
|
||||
.and_then(|x| x.get("Services"))
|
||||
.and_then(|x| x.as_dictionary())
|
||||
{
|
||||
Some(d) => d,
|
||||
None => return Err(IdeviceError::UnexpectedResponse),
|
||||
};
|
||||
|
||||
// Parse available services
|
||||
let mut services: HashMap<String, RsdService> = HashMap::new();
|
||||
for (name, service) in services_dict.into_iter() {
|
||||
match service.as_dictionary() {
|
||||
Some(service) => {
|
||||
let entitlement = match service.get("Entitlement").and_then(|x| x.as_string()) {
|
||||
Some(e) => e.to_string(),
|
||||
None => {
|
||||
warn!("Service did not contain entitlement string");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let port = match service
|
||||
.get("Port")
|
||||
.and_then(|x| x.as_string())
|
||||
.and_then(|x| x.parse::<u16>().ok())
|
||||
{
|
||||
Some(e) => e,
|
||||
None => {
|
||||
warn!("Service did not contain port string");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let uses_remote_xpc = match service
|
||||
.get("Properties")
|
||||
.and_then(|x| x.as_dictionary())
|
||||
.and_then(|x| x.get("UsesRemoteXPC"))
|
||||
.and_then(|x| x.as_boolean())
|
||||
{
|
||||
Some(e) => e.to_owned(),
|
||||
None => false, // default is false
|
||||
};
|
||||
|
||||
let features = service
|
||||
.get("Properties")
|
||||
.and_then(|x| x.as_dictionary())
|
||||
.and_then(|x| x.get("Features"))
|
||||
.and_then(|x| x.as_array())
|
||||
.map(|f| {
|
||||
f.iter()
|
||||
.filter_map(|x| x.as_string())
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
});
|
||||
|
||||
let service_version = service
|
||||
.get("Properties")
|
||||
.and_then(|x| x.as_dictionary())
|
||||
.and_then(|x| x.get("ServiceVersion"))
|
||||
.and_then(|x| x.as_signed_integer())
|
||||
.map(|e| e.to_owned());
|
||||
|
||||
services.insert(
|
||||
name.to_string(),
|
||||
RsdService {
|
||||
entitlement,
|
||||
port,
|
||||
uses_remote_xpc,
|
||||
features,
|
||||
service_version,
|
||||
},
|
||||
);
|
||||
}
|
||||
None => {
|
||||
warn!("Service is not a dictionary!");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let protocol_version = match data.as_dictionary().and_then(|x| {
|
||||
x.get("MessagingProtocolVersion")
|
||||
.and_then(|x| x.as_signed_integer())
|
||||
}) {
|
||||
Some(p) => p as usize,
|
||||
None => {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
};
|
||||
|
||||
let uuid = match data
|
||||
.as_dictionary()
|
||||
.and_then(|x| x.get("UUID").and_then(|x| x.as_string()))
|
||||
{
|
||||
Some(u) => u.to_string(),
|
||||
None => {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
};
|
||||
|
||||
let properties = match data
|
||||
.as_dictionary()
|
||||
.and_then(|x| x.get("Properties").and_then(|x| x.as_dictionary()))
|
||||
{
|
||||
Some(d) => d
|
||||
.into_iter()
|
||||
.map(|(name, prop)| (name.to_owned(), prop.to_owned()))
|
||||
.collect::<HashMap<String, plist::Value>>(),
|
||||
None => {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
services,
|
||||
protocol_version,
|
||||
properties,
|
||||
uuid,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn connect<'a, T, S>(
|
||||
&mut self,
|
||||
provider: &'a mut impl RsdProvider<'a, Stream = S>,
|
||||
) -> Result<T, IdeviceError>
|
||||
where
|
||||
T: crate::RsdService<Stream = S>,
|
||||
S: ReadWrite,
|
||||
{
|
||||
let service_name = T::rsd_service_name();
|
||||
let service = match self.services.get(service_name) {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return Err(IdeviceError::ServiceNotFound);
|
||||
}
|
||||
};
|
||||
|
||||
let stream = provider.connect_to_service_port(service.port).await?;
|
||||
T::from_stream(stream).await
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
// DebianArch
|
||||
|
||||
use super::http2::error::Http2Error;
|
||||
use std::{
|
||||
array::TryFromSliceError, error::Error, ffi::FromVecWithNulError, io, num::TryFromIntError,
|
||||
str::Utf8Error,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum XPCError {
|
||||
Io(io::Error),
|
||||
Http2Error(Http2Error),
|
||||
ParseError(ParseError),
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ParseError {
|
||||
TryFromSliceError(TryFromSliceError),
|
||||
TryFromIntError(TryFromIntError),
|
||||
FromVecWithNulError(FromVecWithNulError),
|
||||
Utf8Error(Utf8Error),
|
||||
}
|
||||
|
||||
impl From<TryFromSliceError> for XPCError {
|
||||
fn from(value: TryFromSliceError) -> Self {
|
||||
Self::ParseError(ParseError::TryFromSliceError(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TryFromIntError> for XPCError {
|
||||
fn from(value: TryFromIntError) -> Self {
|
||||
Self::ParseError(ParseError::TryFromIntError(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError> for XPCError {
|
||||
fn from(value: ParseError) -> Self {
|
||||
Self::ParseError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromVecWithNulError> for XPCError {
|
||||
fn from(value: FromVecWithNulError) -> Self {
|
||||
Self::ParseError(ParseError::FromVecWithNulError(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Utf8Error> for XPCError {
|
||||
fn from(value: Utf8Error) -> Self {
|
||||
Self::ParseError(ParseError::Utf8Error(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for XPCError {
|
||||
fn from(value: io::Error) -> Self {
|
||||
Self::Io(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for XPCError {
|
||||
fn from(value: &str) -> Self {
|
||||
Self::Custom(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Http2Error> for XPCError {
|
||||
fn from(value: Http2Error) -> Self {
|
||||
Self::Http2Error(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for XPCError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"XPCError({})",
|
||||
match self {
|
||||
Self::Io(io) => io.to_string(),
|
||||
Self::Http2Error(http2) => http2.to_string(),
|
||||
Self::ParseError(e) => e.to_string(),
|
||||
Self::Custom(s) => s.clone(),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"ParseError({})",
|
||||
match self {
|
||||
Self::TryFromSliceError(e) => e.to_string(),
|
||||
Self::TryFromIntError(e) => e.to_string(),
|
||||
Self::FromVecWithNulError(e) => e.to_string(),
|
||||
Self::Utf8Error(e) => e.to_string(),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for XPCError {}
|
||||
@@ -1,62 +0,0 @@
|
||||
// DebianArch
|
||||
|
||||
use std::{array::TryFromSliceError, error::Error, io, num::TryFromIntError};
|
||||
|
||||
use tokio::sync::mpsc::error::SendError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Http2Error {
|
||||
Io(io::Error),
|
||||
SendError,
|
||||
TryFromIntError(TryFromIntError),
|
||||
TryFromSliceError(TryFromSliceError),
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl From<io::Error> for Http2Error {
|
||||
fn from(value: io::Error) -> Self {
|
||||
Self::Io(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<SendError<T>> for Http2Error {
|
||||
fn from(_: SendError<T>) -> Self {
|
||||
Self::SendError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Http2Error {
|
||||
fn from(value: &str) -> Self {
|
||||
Self::Custom(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TryFromIntError> for Http2Error {
|
||||
fn from(value: TryFromIntError) -> Self {
|
||||
Self::TryFromIntError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TryFromSliceError> for Http2Error {
|
||||
fn from(value: TryFromSliceError) -> Self {
|
||||
Self::TryFromSliceError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Http2Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Http2Error({})",
|
||||
match self {
|
||||
Self::Io(io) => io.to_string(),
|
||||
Self::SendError => "SendError".to_string(),
|
||||
Self::TryFromIntError(e) => e.to_string(),
|
||||
Self::TryFromSliceError(e) => e.to_string(),
|
||||
Self::Custom(s) => s.clone(),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for Http2Error {}
|
||||
@@ -1,256 +0,0 @@
|
||||
// DebianArch
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::error::Http2Error;
|
||||
|
||||
pub const HTTP2_MAGIC: &[u8; 24] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Frame {
|
||||
pub stream_id: u32,
|
||||
pub flags: u8,
|
||||
pub frame_type: FrameType,
|
||||
|
||||
pub body: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
pub fn deserialize(buf: &[u8]) -> Result<Self, Http2Error> {
|
||||
let mut len_buf = buf[0..3].to_vec();
|
||||
len_buf.insert(0, 0);
|
||||
|
||||
let body_len = u32::from_be_bytes(len_buf.try_into().unwrap()) as usize;
|
||||
let frame_type = buf[3];
|
||||
let flags = buf[4];
|
||||
let stream_id = u32::from_be_bytes(buf[5..9].try_into()?);
|
||||
let body = buf[9..9 + body_len].to_vec();
|
||||
Ok(Self {
|
||||
stream_id,
|
||||
flags,
|
||||
frame_type: frame_type.into(),
|
||||
body,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Framable for Frame {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut res = Vec::new();
|
||||
|
||||
let body_len = (self.body.len() as u32).to_be_bytes();
|
||||
res.extend_from_slice(&[body_len[1], body_len[2], body_len[3]]); // [0..3]
|
||||
res.push(self.frame_type.into()); // [3]
|
||||
res.push(self.flags); // flag mask [4]
|
||||
res.extend_from_slice(&self.stream_id.to_be_bytes()); // [4..8]
|
||||
res.extend_from_slice(&self.body); // [9..9+len]
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Framable: From<Frame> {
|
||||
fn serialize(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
// Frame implementations:
|
||||
pub struct SettingsFrame {
|
||||
frame: Frame,
|
||||
pub settings: HashMap<u16, u32>,
|
||||
}
|
||||
|
||||
impl SettingsFrame {
|
||||
pub const MAX_CONCURRENT_STREAMS: u16 = 0x03;
|
||||
pub const INITIAL_WINDOW_SIZE: u16 = 0x04;
|
||||
|
||||
pub const ACK: u8 = 0x01;
|
||||
pub fn new(/*stream_id: u32, */ settings: HashMap<u16, u32>, flags: u8) -> Self {
|
||||
let mut body = Vec::new();
|
||||
for setting in settings.clone() {
|
||||
body.extend_from_slice(&setting.0.to_be_bytes());
|
||||
body.extend_from_slice(&setting.1.to_be_bytes());
|
||||
}
|
||||
Self {
|
||||
frame: Frame {
|
||||
/*stream_id*/ stream_id: 0,
|
||||
flags,
|
||||
frame_type: FrameType::Settings,
|
||||
body,
|
||||
},
|
||||
settings,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ack(/*stream_id: u32*/) -> Self {
|
||||
Self {
|
||||
frame: Frame {
|
||||
/*stream_id*/ stream_id: 0,
|
||||
flags: Self::ACK,
|
||||
frame_type: FrameType::Settings,
|
||||
body: Vec::new(),
|
||||
},
|
||||
settings: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Framable for SettingsFrame {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
self.frame.serialize()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Frame> for SettingsFrame {
|
||||
fn from(value: Frame) -> Self {
|
||||
let mut idx = 0;
|
||||
let mut settings = HashMap::new();
|
||||
while idx < value.body.len() {
|
||||
let key = u16::from_be_bytes(value.body[idx..idx + 2].try_into().unwrap());
|
||||
let value = u32::from_be_bytes(value.body[idx + 2..idx + 2 + 4].try_into().unwrap());
|
||||
settings.insert(key, value);
|
||||
idx += 2 + 4;
|
||||
}
|
||||
Self {
|
||||
frame: value,
|
||||
settings,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WindowUpdateFrame {
|
||||
frame: Frame,
|
||||
}
|
||||
|
||||
impl WindowUpdateFrame {
|
||||
// the frame's stream identifier indicates the affected stream; in the latter, the value "0" indicates that the entire connection is the subject of the frame.
|
||||
pub fn new(stream_id: u32, window_increment: u32) -> Self {
|
||||
if window_increment == 0 {
|
||||
panic!("PROTOCOL_ERROR");
|
||||
}
|
||||
Self {
|
||||
frame: Frame {
|
||||
stream_id,
|
||||
flags: Default::default(),
|
||||
frame_type: FrameType::WindowUpdate,
|
||||
body: window_increment.to_be_bytes().to_vec(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Framable for WindowUpdateFrame {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
self.frame.serialize()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Frame> for WindowUpdateFrame {
|
||||
fn from(value: Frame) -> Self {
|
||||
Self { frame: value }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HeadersFrame {
|
||||
frame: Frame,
|
||||
}
|
||||
|
||||
impl HeadersFrame {
|
||||
pub const END_HEADERS: u8 = 0x04;
|
||||
pub const PADDED: u8 = 0x08;
|
||||
pub const PRIORITY: u8 = 0x20;
|
||||
pub fn new(stream_id: u32, flags: u8) -> Self {
|
||||
Self {
|
||||
frame: Frame {
|
||||
stream_id,
|
||||
flags,
|
||||
frame_type: FrameType::Headers,
|
||||
body: Vec::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Framable for HeadersFrame {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
if self.frame.flags & Self::PADDED == Self::PADDED {
|
||||
unimplemented!("haven't added padding support !")
|
||||
};
|
||||
|
||||
if self.frame.flags & Self::PRIORITY == Self::PRIORITY {
|
||||
unimplemented!("haven't added priority support !")
|
||||
};
|
||||
|
||||
// let padding = 0; for 'PADDED' flag
|
||||
// let priority_data = b""; // for PRIORITY flag
|
||||
self.frame.serialize()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Frame> for HeadersFrame {
|
||||
fn from(value: Frame) -> Self {
|
||||
if value.flags & Self::PADDED == Self::PADDED {
|
||||
unimplemented!("haven't added padding support !")
|
||||
};
|
||||
|
||||
if value.flags & Self::PRIORITY == Self::PRIORITY {
|
||||
unimplemented!("haven't added priority support !")
|
||||
};
|
||||
Self { frame: value }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DataFrame {
|
||||
frame: Frame,
|
||||
}
|
||||
|
||||
impl DataFrame {
|
||||
// TODO: Handle padding flag.
|
||||
pub fn new(stream_id: u32, data: Vec<u8>, flags: u8) -> Self {
|
||||
Self {
|
||||
frame: Frame {
|
||||
stream_id,
|
||||
flags,
|
||||
frame_type: FrameType::Data,
|
||||
body: data,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Framable for DataFrame {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
self.frame.serialize()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Frame> for DataFrame {
|
||||
fn from(value: Frame) -> Self {
|
||||
Self { frame: value }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum FrameType {
|
||||
Data = 0,
|
||||
Headers = 1,
|
||||
Priority = 2,
|
||||
RstStream = 3,
|
||||
Settings = 4,
|
||||
PushPromise = 5,
|
||||
Ping = 6,
|
||||
GoAway = 7,
|
||||
WindowUpdate = 8,
|
||||
Continuation = 9,
|
||||
}
|
||||
|
||||
impl From<FrameType> for u8 {
|
||||
fn from(value: FrameType) -> Self {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for FrameType {
|
||||
fn from(value: u8) -> Self {
|
||||
unsafe { std::mem::transmute::<_, FrameType>(value) }
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
// DebianArch
|
||||
|
||||
use async_recursion::async_recursion;
|
||||
use error::Http2Error;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
sync::mpsc::{self, Receiver, Sender},
|
||||
};
|
||||
|
||||
pub mod error;
|
||||
pub mod h2;
|
||||
|
||||
use h2::{
|
||||
DataFrame, Framable, Frame, FrameType, HeadersFrame, SettingsFrame, WindowUpdateFrame,
|
||||
HTTP2_MAGIC,
|
||||
};
|
||||
|
||||
use crate::ReadWrite;
|
||||
|
||||
pub type Channels = HashMap<u32, (Sender<Vec<u8>>, Receiver<Vec<u8>>)>;
|
||||
|
||||
pub const INIT_STREAM: u32 = 0;
|
||||
pub const ROOT_CHANNEL: u32 = 1;
|
||||
pub const REPLY_CHANNEL: u32 = 3;
|
||||
|
||||
pub struct Connection<R: ReadWrite> {
|
||||
pub stream: R,
|
||||
channels: Channels,
|
||||
window_size: u32,
|
||||
}
|
||||
|
||||
impl<R: ReadWrite> Connection<R> {
|
||||
pub async fn new(mut stream: R) -> Result<Self, Http2Error> {
|
||||
stream.write_all(HTTP2_MAGIC).await?;
|
||||
Ok(Self {
|
||||
stream,
|
||||
channels: HashMap::new(),
|
||||
window_size: 1048576,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_frame<A: Framable>(&mut self, frame: A) -> Result<(), Http2Error> {
|
||||
let body = &frame.serialize();
|
||||
if body.len() > self.window_size as usize {
|
||||
panic!("we need to chunk it :D")
|
||||
}
|
||||
self.stream.write_all(body).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read_data(&mut self) -> Result<Vec<u8>, Http2Error> {
|
||||
loop {
|
||||
let frame = self.read_frame().await?;
|
||||
match frame.frame_type {
|
||||
FrameType::Data => {
|
||||
if frame.stream_id % 2 == 0 && !frame.body.is_empty() {
|
||||
let frame_len: u32 = frame.body.len().try_into()?;
|
||||
self.send_frame(WindowUpdateFrame::new(0, frame_len))
|
||||
.await?;
|
||||
self.send_frame(WindowUpdateFrame::new(frame.stream_id, frame_len))
|
||||
.await?;
|
||||
}
|
||||
match self.channels.get_mut(&frame.stream_id) {
|
||||
Some((sender, _receiver)) => {
|
||||
sender.send(frame.body.clone()).await?;
|
||||
}
|
||||
None => {
|
||||
let chan = mpsc::channel(100);
|
||||
chan.0.send(frame.body.clone()).await?;
|
||||
self.channels.insert(frame.stream_id, chan);
|
||||
}
|
||||
}
|
||||
return Ok(frame.body);
|
||||
}
|
||||
FrameType::GoAway | FrameType::RstStream => {
|
||||
let _last_streamid = u32::from_be_bytes(frame.body[0..4].try_into().unwrap());
|
||||
return Err("connection closed, bye")?;
|
||||
}
|
||||
FrameType::Settings => {
|
||||
let flags = frame.flags;
|
||||
let settings_frame: SettingsFrame = frame.into();
|
||||
if flags & SettingsFrame::ACK != SettingsFrame::ACK {
|
||||
self.send_frame(SettingsFrame::ack()).await?;
|
||||
}
|
||||
if let Some(&window_size) = settings_frame
|
||||
.settings
|
||||
.get(&SettingsFrame::INITIAL_WINDOW_SIZE)
|
||||
{
|
||||
self.window_size = window_size;
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read_frame(&mut self) -> Result<Frame, Http2Error> {
|
||||
let mut length_buf = vec![0; 3];
|
||||
self.stream.read_exact(&mut length_buf).await?;
|
||||
length_buf.insert(0, 0);
|
||||
let len = u32::from_be_bytes(length_buf.clone().try_into().unwrap()) as usize;
|
||||
let mut rest = vec![0; 9 - 3 + len];
|
||||
self.stream.read_exact(&mut rest).await?;
|
||||
|
||||
let mut content = vec![length_buf[1], length_buf[2], length_buf[3]];
|
||||
content.extend_from_slice(&rest);
|
||||
Frame::deserialize(&content)
|
||||
}
|
||||
|
||||
// pub async fn multiplex_write(&mut self, stream_id: u32) -> Result<()> {}
|
||||
|
||||
// gets a Reader + Writer for a channel.
|
||||
pub async fn write_streamid(
|
||||
&mut self,
|
||||
stream_id: u32,
|
||||
data: Vec<u8>,
|
||||
) -> Result<(), Http2Error> {
|
||||
// TODO: If we ever allow concurrent writes we must not always send 'END_HEADERS'.
|
||||
self.send_frame(HeadersFrame::new(stream_id, HeadersFrame::END_HEADERS))
|
||||
.await?;
|
||||
self.send_frame(DataFrame::new(stream_id, data, Default::default()))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_recursion]
|
||||
pub async fn read_streamid(&mut self, stream_id: u32) -> Result<Vec<u8>, Http2Error> {
|
||||
match self.channels.get_mut(&stream_id) {
|
||||
Some((_sender, receiver)) => match receiver.try_recv().ok() {
|
||||
Some(data) => Ok(data),
|
||||
None => {
|
||||
self.read_data().await?;
|
||||
self.read_streamid(stream_id).await
|
||||
}
|
||||
},
|
||||
None => {
|
||||
self.read_data().await?;
|
||||
self.read_streamid(stream_id).await
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
//! XPC (Cross-Process Communication) Implementation
|
||||
//!
|
||||
//! Provides functionality for interacting with Apple's XPC protocol over HTTP/2,
|
||||
//! which is used for inter-process communication between iOS/macOS components.
|
||||
|
||||
use std::collections::HashMap;
|
||||
mod http2;
|
||||
|
||||
use crate::{IdeviceError, ReadWrite};
|
||||
use error::XPCError;
|
||||
use format::{XPCFlag, XPCMessage, XPCObject};
|
||||
use http2::h2::{SettingsFrame, WindowUpdateFrame};
|
||||
use log::{debug, warn};
|
||||
use serde::Deserialize;
|
||||
|
||||
pub mod error;
|
||||
mod format;
|
||||
|
||||
/// Represents an XPC connection to a device with available services
|
||||
pub struct XPCDevice<R: ReadWrite> {
|
||||
/// The underlying XPC connection
|
||||
pub connection: XPCConnection<R>,
|
||||
/// Map of available XPC services by name
|
||||
pub services: HashMap<String, XPCService>,
|
||||
}
|
||||
|
||||
/// Describes an available XPC service
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct XPCService {
|
||||
/// Required entitlement to access this service
|
||||
pub entitlement: String,
|
||||
/// Port number where the service is available
|
||||
pub port: u16,
|
||||
/// Whether the service uses remote XPC
|
||||
pub uses_remote_xpc: bool,
|
||||
/// Optional list of supported features
|
||||
pub features: Option<Vec<String>>,
|
||||
/// Optional service version number
|
||||
pub service_version: Option<i64>,
|
||||
}
|
||||
|
||||
/// Manages an active XPC connection over HTTP/2
|
||||
pub struct XPCConnection<R: ReadWrite> {
|
||||
pub(crate) inner: http2::Connection<R>,
|
||||
root_message_id: u64,
|
||||
reply_message_id: u64,
|
||||
}
|
||||
|
||||
impl<R: ReadWrite> XPCDevice<R> {
|
||||
/// Creates a new XPC device connection
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `stream` - The underlying transport stream
|
||||
///
|
||||
/// # Returns
|
||||
/// A connected XPCDevice instance with discovered services
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns `IdeviceError` if:
|
||||
/// - The connection fails
|
||||
/// - The service discovery response is malformed
|
||||
pub async fn new(stream: R) -> Result<Self, IdeviceError> {
|
||||
let mut connection = XPCConnection::new(stream).await?;
|
||||
|
||||
// Read initial services message
|
||||
let data = connection.read_message(http2::ROOT_CHANNEL).await?;
|
||||
|
||||
let data = match data.message {
|
||||
Some(d) => match d
|
||||
.as_dictionary()
|
||||
.and_then(|x| x.get("Services"))
|
||||
.and_then(|x| x.as_dictionary())
|
||||
{
|
||||
Some(d) => d.to_owned(),
|
||||
None => return Err(IdeviceError::UnexpectedResponse),
|
||||
},
|
||||
None => return Err(IdeviceError::UnexpectedResponse),
|
||||
};
|
||||
|
||||
// Parse available services
|
||||
let mut services = HashMap::new();
|
||||
for (name, service) in data.into_iter() {
|
||||
match service.as_dictionary() {
|
||||
Some(service) => {
|
||||
let entitlement = match service.get("Entitlement").and_then(|x| x.as_string()) {
|
||||
Some(e) => e.to_string(),
|
||||
None => {
|
||||
warn!("Service did not contain entitlement string");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let port = match service
|
||||
.get("Port")
|
||||
.and_then(|x| x.as_string())
|
||||
.and_then(|x| x.parse::<u16>().ok())
|
||||
{
|
||||
Some(e) => e,
|
||||
None => {
|
||||
warn!("Service did not contain port string");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let uses_remote_xpc = match service
|
||||
.get("Properties")
|
||||
.and_then(|x| x.as_dictionary())
|
||||
.and_then(|x| x.get("UsesRemoteXPC"))
|
||||
.and_then(|x| x.as_bool())
|
||||
{
|
||||
Some(e) => e.to_owned(),
|
||||
None => false, // default is false
|
||||
};
|
||||
|
||||
let features = service
|
||||
.get("Properties")
|
||||
.and_then(|x| x.as_dictionary())
|
||||
.and_then(|x| x.get("Features"))
|
||||
.and_then(|x| x.as_array())
|
||||
.map(|f| {
|
||||
f.iter()
|
||||
.filter_map(|x| x.as_string())
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
});
|
||||
|
||||
let service_version = service
|
||||
.get("Properties")
|
||||
.and_then(|x| x.as_dictionary())
|
||||
.and_then(|x| x.get("ServiceVersion"))
|
||||
.and_then(|x| x.as_signed_integer())
|
||||
.map(|e| e.to_owned());
|
||||
|
||||
services.insert(
|
||||
name,
|
||||
XPCService {
|
||||
entitlement,
|
||||
port,
|
||||
uses_remote_xpc,
|
||||
features,
|
||||
service_version,
|
||||
},
|
||||
);
|
||||
}
|
||||
None => {
|
||||
warn!("Service is not a dictionary!");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
connection,
|
||||
services,
|
||||
})
|
||||
}
|
||||
|
||||
/// Consumes the device and returns the underlying transport stream
|
||||
pub fn into_inner(self) -> R {
|
||||
self.connection.inner.stream
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ReadWrite> XPCConnection<R> {
|
||||
/// Channel ID for root messages
|
||||
pub const ROOT_CHANNEL: u32 = http2::ROOT_CHANNEL;
|
||||
/// Channel ID for reply messages
|
||||
pub const REPLY_CHANNEL: u32 = http2::REPLY_CHANNEL;
|
||||
/// Initial stream ID for HTTP/2 connection
|
||||
const INIT_STREAM: u32 = http2::INIT_STREAM;
|
||||
|
||||
/// Establishes a new XPC connection
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `stream` - The underlying transport stream
|
||||
///
|
||||
/// # Returns
|
||||
/// A connected XPCConnection instance
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns `XPCError` if the connection handshake fails
|
||||
pub async fn new(stream: R) -> Result<Self, XPCError> {
|
||||
let mut client = http2::Connection::new(stream).await?;
|
||||
|
||||
// Configure HTTP/2 settings
|
||||
client
|
||||
.send_frame(SettingsFrame::new(
|
||||
[
|
||||
(SettingsFrame::MAX_CONCURRENT_STREAMS, 100),
|
||||
(SettingsFrame::INITIAL_WINDOW_SIZE, 1048576),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
Default::default(),
|
||||
))
|
||||
.await?;
|
||||
|
||||
// Update window size
|
||||
client
|
||||
.send_frame(WindowUpdateFrame::new(Self::INIT_STREAM, 983041))
|
||||
.await?;
|
||||
|
||||
let mut xpc_client = Self {
|
||||
inner: client,
|
||||
root_message_id: 1,
|
||||
reply_message_id: 1,
|
||||
};
|
||||
|
||||
// Perform XPC handshake
|
||||
xpc_client
|
||||
.send_recv_message(
|
||||
Self::ROOT_CHANNEL,
|
||||
XPCMessage::new(
|
||||
Some(XPCFlag::AlwaysSet),
|
||||
Some(XPCObject::Dictionary(Default::default())),
|
||||
None,
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
||||
xpc_client
|
||||
.send_recv_message(
|
||||
Self::REPLY_CHANNEL,
|
||||
XPCMessage::new(
|
||||
Some(XPCFlag::InitHandshake | XPCFlag::AlwaysSet),
|
||||
None,
|
||||
None,
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
||||
xpc_client
|
||||
.send_recv_message(
|
||||
Self::ROOT_CHANNEL,
|
||||
XPCMessage::new(Some(XPCFlag::Custom(0x201)), None, None),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(xpc_client)
|
||||
}
|
||||
|
||||
/// Sends a message and waits for the response
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `stream_id` - The channel/stream to use
|
||||
/// * `message` - The XPC message to send
|
||||
///
|
||||
/// # Returns
|
||||
/// The response message
|
||||
pub async fn send_recv_message(
|
||||
&mut self,
|
||||
stream_id: u32,
|
||||
message: XPCMessage,
|
||||
) -> Result<XPCMessage, XPCError> {
|
||||
self.send_message(stream_id, message).await?;
|
||||
self.read_message(stream_id).await
|
||||
}
|
||||
|
||||
/// Sends an XPC message without waiting for a response
|
||||
pub async fn send_message(
|
||||
&mut self,
|
||||
stream_id: u32,
|
||||
message: XPCMessage,
|
||||
) -> Result<(), XPCError> {
|
||||
self.inner
|
||||
.write_streamid(stream_id, message.encode(self.root_message_id)?)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads an XPC message from the specified stream
|
||||
pub async fn read_message(&mut self, stream_id: u32) -> Result<XPCMessage, XPCError> {
|
||||
let mut buf = self.inner.read_streamid(stream_id).await?;
|
||||
loop {
|
||||
match XPCMessage::decode(&buf) {
|
||||
Ok(decoded) => {
|
||||
debug!("Decoded message: {:?}", decoded);
|
||||
match stream_id {
|
||||
1 => self.root_message_id += 1,
|
||||
3 => self.reply_message_id += 1,
|
||||
_ => {}
|
||||
}
|
||||
return Ok(decoded);
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("Error decoding message: {:?}", err);
|
||||
buf.extend_from_slice(&self.inner.read_streamid(stream_id).await?);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,7 @@
|
||||
//! - Optional PCAP packet capture
|
||||
//! - Implements `AsyncRead` and `AsyncWrite` for Tokio compatibility
|
||||
//!
|
||||
//! # Limitations
|
||||
//! - Only supports one connection at a time
|
||||
//! # Limitations (unecessary for CDTunnel)
|
||||
//! - No proper sequence number tracking
|
||||
//! - No retransmission or congestion control
|
||||
//! - Requires 100% reliable underlying transport
|
||||
@@ -22,8 +21,8 @@
|
||||
//! ```rust,no_run
|
||||
//! use std::net::{IpAddr, Ipv4Addr};
|
||||
//! use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
//! use your_crate::tcp::Adapter;
|
||||
//! use your_crate::ReadWrite; // Assuming you have a ReadWrite trait
|
||||
//! use idevice::tcp::{adapter::Adapter, stream::AdapterStream};
|
||||
//! use idevice::ReadWrite;
|
||||
//!
|
||||
//! #[tokio::main]
|
||||
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -39,19 +38,19 @@
|
||||
//! adapter.pcap("capture.pcap").await?;
|
||||
//!
|
||||
//! // Connect to remote server
|
||||
//! adapter.connect(80).await?;
|
||||
//! let stream = AdapterStream::new(&mut adapter, 80).await?;
|
||||
//!
|
||||
//! // Send HTTP request
|
||||
//! adapter.write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n").await?;
|
||||
//! adapter.flush().await?;
|
||||
//! stream.write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n").await?;
|
||||
//! stream.flush().await?;
|
||||
//!
|
||||
//! // Read response
|
||||
//! let mut buf = vec![0; 1024];
|
||||
//! let n = adapter.read(&mut buf).await?;
|
||||
//! let n = stream.read(&mut buf).await?;
|
||||
//! println!("Received: {}", String::from_utf8_lossy(&buf[..n]));
|
||||
//!
|
||||
//! // Close connection
|
||||
//! adapter.close().await?;
|
||||
//! stream.close().await?;
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
@@ -59,35 +58,55 @@
|
||||
//!
|
||||
//! # Warning
|
||||
//! This implementation makes significant simplifications and should not be used
|
||||
//! in production environments or with unreliable network transports.
|
||||
//! with unreliable network transports.
|
||||
|
||||
use std::{future::Future, net::IpAddr, path::Path, sync::Arc, task::Poll};
|
||||
use std::{collections::HashMap, io::ErrorKind, net::IpAddr, path::Path, sync::Arc};
|
||||
|
||||
use log::trace;
|
||||
use tokio::{
|
||||
io::{AsyncRead, AsyncWrite, AsyncWriteExt},
|
||||
sync::Mutex,
|
||||
};
|
||||
use tokio::{io::AsyncWriteExt, sync::Mutex};
|
||||
|
||||
use crate::ReadWrite;
|
||||
|
||||
use super::packets::{Ipv4Packet, Ipv6Packet, ProtocolNumber, TcpFlags, TcpPacket};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum AdapterState {
|
||||
#[derive(Debug, Clone)]
|
||||
struct ConnectionState {
|
||||
seq: u32,
|
||||
ack: u32,
|
||||
host_port: u16,
|
||||
peer_port: u16,
|
||||
read_buffer: Vec<u8>,
|
||||
write_buffer: Vec<u8>,
|
||||
status: ConnectionStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum ConnectionStatus {
|
||||
WaitingForSyn,
|
||||
Connected,
|
||||
None,
|
||||
Error(ErrorKind),
|
||||
}
|
||||
|
||||
impl ConnectionState {
|
||||
fn new(host_port: u16, peer_port: u16) -> Self {
|
||||
Self {
|
||||
seq: rand::random(),
|
||||
ack: 0,
|
||||
host_port,
|
||||
peer_port,
|
||||
read_buffer: Vec::new(),
|
||||
write_buffer: Vec::new(),
|
||||
status: ConnectionStatus::WaitingForSyn,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A simplified TCP network stack implementation.
|
||||
///
|
||||
/// This is an extremely naive, limited, and dangerous TCP stack implementation.
|
||||
/// Key limitations:
|
||||
/// - Only one connection can be active at a time
|
||||
/// - ACKs aren't properly tracked and are silently ignored
|
||||
/// - Should only be used when the underlying transport is 100% reliable
|
||||
///
|
||||
/// The adapter implements `AsyncRead` and `AsyncWrite` for convenient IO operations.
|
||||
#[derive(Debug)]
|
||||
pub struct Adapter {
|
||||
/// The underlying transport connection
|
||||
@@ -96,26 +115,11 @@ pub struct Adapter {
|
||||
host_ip: IpAddr,
|
||||
/// The remote peer's IP address
|
||||
peer_ip: IpAddr,
|
||||
/// Current connection state
|
||||
state: AdapterState,
|
||||
|
||||
// TCP state
|
||||
/// Current sequence number
|
||||
seq: u32,
|
||||
/// Current acknowledgement number
|
||||
ack: u32,
|
||||
/// Local port number
|
||||
host_port: u16,
|
||||
/// Remote port number
|
||||
peer_port: u16,
|
||||
/// The states of the connections
|
||||
states: HashMap<u16, ConnectionState>, // host port by state
|
||||
dropped: Vec<u16>,
|
||||
|
||||
// Read buffer to cache unused bytes
|
||||
/// Buffer for storing unread received data
|
||||
read_buffer: Vec<u8>,
|
||||
/// Buffer for storing data to be sent
|
||||
write_buffer: Vec<u8>,
|
||||
|
||||
// Logging
|
||||
/// Optional PCAP file for packet logging
|
||||
pcap: Option<Arc<Mutex<tokio::fs::File>>>,
|
||||
}
|
||||
@@ -129,19 +133,14 @@ impl Adapter {
|
||||
/// * `peer_ip` - The remote IP address to connect to
|
||||
///
|
||||
/// # Returns
|
||||
/// A new unconnected `Adapter` instance
|
||||
/// A new `Adapter` instance
|
||||
pub fn new(peer: Box<dyn ReadWrite>, host_ip: IpAddr, peer_ip: IpAddr) -> Self {
|
||||
Self {
|
||||
peer,
|
||||
host_ip,
|
||||
peer_ip,
|
||||
state: AdapterState::None,
|
||||
seq: 0,
|
||||
ack: 0,
|
||||
host_port: 1024,
|
||||
peer_port: 1024,
|
||||
read_buffer: Vec::new(),
|
||||
write_buffer: Vec::new(),
|
||||
states: HashMap::new(),
|
||||
dropped: Vec::new(),
|
||||
pcap: None,
|
||||
}
|
||||
}
|
||||
@@ -152,32 +151,31 @@ impl Adapter {
|
||||
/// * `port` - The remote port number to connect to
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Ok(())` if connection was successful
|
||||
/// * `Ok(u16)` the chosen host port if successful
|
||||
/// * `Err(std::io::Error)` if connection failed
|
||||
///
|
||||
/// # Errors
|
||||
/// * Returns `InvalidData` if the SYN-ACK response is invalid
|
||||
/// * Returns other IO errors if underlying transport fails
|
||||
pub async fn connect(&mut self, port: u16) -> Result<(), std::io::Error> {
|
||||
self.read_buffer = Vec::new();
|
||||
self.write_buffer = Vec::new();
|
||||
|
||||
// Randomize seq
|
||||
self.seq = rand::random();
|
||||
self.ack = 0;
|
||||
|
||||
// Choose a random port
|
||||
self.host_port = rand::random();
|
||||
self.peer_port = port;
|
||||
pub(crate) async fn connect(&mut self, port: u16) -> Result<u16, std::io::Error> {
|
||||
let host_port = loop {
|
||||
let host_port: u16 = rand::random();
|
||||
if self.states.contains_key(&host_port) {
|
||||
continue;
|
||||
} else {
|
||||
break host_port;
|
||||
}
|
||||
};
|
||||
let state = ConnectionState::new(host_port, port);
|
||||
|
||||
// Create the TCP packet
|
||||
let tcp_packet = TcpPacket::create(
|
||||
self.host_ip,
|
||||
self.peer_ip,
|
||||
self.host_port,
|
||||
self.peer_port,
|
||||
self.seq,
|
||||
self.ack,
|
||||
state.host_port,
|
||||
state.peer_port,
|
||||
state.seq,
|
||||
state.ack,
|
||||
TcpFlags {
|
||||
syn: true,
|
||||
..Default::default()
|
||||
@@ -187,24 +185,28 @@ impl Adapter {
|
||||
);
|
||||
let ip_packet = self.ip_wrap(&tcp_packet);
|
||||
self.peer.write_all(&ip_packet).await?;
|
||||
self.log_packet(&ip_packet).await?;
|
||||
self.log_packet(&ip_packet)?;
|
||||
|
||||
// Wait for the syn ack
|
||||
let res = self.read_tcp_packet().await?;
|
||||
if !(res.flags.syn && res.flags.ack) {
|
||||
log::error!("Didn't get syn ack: {res:#?}, {self:#?}");
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"No syn ack",
|
||||
));
|
||||
self.states.insert(host_port, state);
|
||||
loop {
|
||||
self.process_tcp_packet().await?;
|
||||
if let Some(s) = self.states.get(&host_port) {
|
||||
match s.status {
|
||||
ConnectionStatus::Connected => {
|
||||
break;
|
||||
}
|
||||
ConnectionStatus::Error(e) => {
|
||||
return Err(std::io::Error::new(e, "failed to connect"))
|
||||
}
|
||||
ConnectionStatus::WaitingForSyn => {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.seq = self.seq.wrapping_add(1);
|
||||
|
||||
// Ack back
|
||||
self.ack().await?;
|
||||
|
||||
self.state = AdapterState::Connected;
|
||||
Ok(())
|
||||
Ok(host_port)
|
||||
}
|
||||
|
||||
/// Enables packet capture to a PCAP file.
|
||||
@@ -232,9 +234,9 @@ impl Adapter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn log_packet(&mut self, packet: &[u8]) -> Result<(), std::io::Error> {
|
||||
fn log_packet(&self, packet: &[u8]) -> Result<(), std::io::Error> {
|
||||
if let Some(file) = &self.pcap {
|
||||
super::log_packet(file, packet).await;
|
||||
super::log_packet(file, packet);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -247,61 +249,63 @@ impl Adapter {
|
||||
///
|
||||
/// # Errors
|
||||
/// * Returns IO errors if underlying transport fails during close
|
||||
pub async fn close(&mut self) -> Result<(), std::io::Error> {
|
||||
let tcp_packet = TcpPacket::create(
|
||||
self.host_ip,
|
||||
self.peer_ip,
|
||||
self.host_port,
|
||||
self.peer_port,
|
||||
self.seq,
|
||||
self.ack,
|
||||
TcpFlags {
|
||||
fin: true,
|
||||
ack: true,
|
||||
..Default::default()
|
||||
},
|
||||
u16::MAX - 1,
|
||||
&[],
|
||||
);
|
||||
let ip_packet = self.ip_wrap(&tcp_packet);
|
||||
self.peer.write_all(&ip_packet).await?;
|
||||
self.log_packet(&ip_packet).await?;
|
||||
pub(crate) async fn close(&mut self, host_port: u16) -> Result<(), std::io::Error> {
|
||||
if let Some(state) = self.states.remove(&host_port) {
|
||||
let tcp_packet = TcpPacket::create(
|
||||
self.host_ip,
|
||||
self.peer_ip,
|
||||
state.host_port,
|
||||
state.peer_port,
|
||||
state.seq,
|
||||
state.ack,
|
||||
TcpFlags {
|
||||
fin: true,
|
||||
ack: true,
|
||||
..Default::default()
|
||||
},
|
||||
u16::MAX - 1,
|
||||
&[],
|
||||
);
|
||||
let ip_packet = self.ip_wrap(&tcp_packet);
|
||||
self.peer.write_all(&ip_packet).await?;
|
||||
self.log_packet(&ip_packet)?;
|
||||
|
||||
loop {
|
||||
let res = self.read_tcp_packet().await?;
|
||||
if res.flags.psh || !res.payload.is_empty() {
|
||||
self.ack().await?;
|
||||
continue;
|
||||
}
|
||||
|
||||
if res.flags.ack || res.flags.fin || res.flags.rst {
|
||||
break;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
ErrorKind::NotConnected,
|
||||
"not connected",
|
||||
))
|
||||
}
|
||||
self.state = AdapterState::None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn ack(&mut self) -> Result<(), std::io::Error> {
|
||||
let tcp_packet = TcpPacket::create(
|
||||
self.host_ip,
|
||||
self.peer_ip,
|
||||
self.host_port,
|
||||
self.peer_port,
|
||||
self.seq,
|
||||
self.ack,
|
||||
TcpFlags {
|
||||
ack: true,
|
||||
..Default::default()
|
||||
},
|
||||
u16::MAX - 1,
|
||||
&[],
|
||||
);
|
||||
let ip_packet = self.ip_wrap(&tcp_packet);
|
||||
self.peer.write_all(&ip_packet).await?;
|
||||
self.log_packet(&ip_packet).await?;
|
||||
async fn ack(&mut self, host_port: u16) -> Result<(), std::io::Error> {
|
||||
if let Some(state) = self.states.get_mut(&host_port) {
|
||||
let tcp_packet = TcpPacket::create(
|
||||
self.host_ip,
|
||||
self.peer_ip,
|
||||
state.host_port,
|
||||
state.peer_port,
|
||||
state.seq,
|
||||
state.ack,
|
||||
TcpFlags {
|
||||
ack: true,
|
||||
..Default::default()
|
||||
},
|
||||
u16::MAX - 1,
|
||||
&[],
|
||||
);
|
||||
let ip_packet = self.ip_wrap(&tcp_packet);
|
||||
self.peer.write_all(&ip_packet).await?;
|
||||
self.log_packet(&ip_packet)?;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
ErrorKind::NotConnected,
|
||||
"not connected",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a TCP packet with PSH flag set (pushing data).
|
||||
@@ -315,44 +319,147 @@ impl Adapter {
|
||||
///
|
||||
/// # Errors
|
||||
/// * Returns IO errors if underlying transport fails
|
||||
pub async fn psh(&mut self, data: &[u8]) -> Result<(), std::io::Error> {
|
||||
trace!("pshing {} bytes", data.len());
|
||||
let tcp_packet = TcpPacket::create(
|
||||
self.host_ip,
|
||||
self.peer_ip,
|
||||
self.host_port,
|
||||
self.peer_port,
|
||||
self.seq,
|
||||
self.ack,
|
||||
TcpFlags {
|
||||
psh: true,
|
||||
ack: true,
|
||||
..Default::default()
|
||||
},
|
||||
u16::MAX - 1,
|
||||
data,
|
||||
);
|
||||
let ip_packet = self.ip_wrap(&tcp_packet);
|
||||
self.peer.write_all(&ip_packet).await?;
|
||||
self.log_packet(&ip_packet).await?;
|
||||
async fn psh(&mut self, data: &[u8], host_port: u16) -> Result<(), std::io::Error> {
|
||||
let data_len = if let Some(state) = self.states.get(&host_port) {
|
||||
// Check to make sure we haven't closed since last operation
|
||||
if let ConnectionStatus::Error(e) = state.status {
|
||||
return Err(std::io::Error::new(e, "socket error"));
|
||||
}
|
||||
trace!("pshing {} bytes", data.len());
|
||||
let tcp_packet = TcpPacket::create(
|
||||
self.host_ip,
|
||||
self.peer_ip,
|
||||
state.host_port,
|
||||
state.peer_port,
|
||||
state.seq,
|
||||
state.ack,
|
||||
TcpFlags {
|
||||
psh: true,
|
||||
ack: true,
|
||||
..Default::default()
|
||||
},
|
||||
u16::MAX - 1,
|
||||
data,
|
||||
);
|
||||
let ip_packet = self.ip_wrap(&tcp_packet);
|
||||
self.peer.write_all(&ip_packet).await?;
|
||||
self.log_packet(&ip_packet)?;
|
||||
data.len() as u32
|
||||
} else {
|
||||
return Err(std::io::Error::new(
|
||||
ErrorKind::NotConnected,
|
||||
"not connected",
|
||||
));
|
||||
};
|
||||
|
||||
self.seq = self.seq.wrapping_add(data.len() as u32);
|
||||
// We have to re-borrow, since we're mutating state
|
||||
if let Some(state) = self.states.get_mut(&host_port) {
|
||||
state.seq = state.seq.wrapping_add(data_len);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn connection_drop(&mut self, host_port: u16) {
|
||||
self.dropped.push(host_port);
|
||||
}
|
||||
|
||||
/// Flushes the packets
|
||||
async fn write_buffer_flush(&mut self) -> Result<(), std::io::Error> {
|
||||
if self.write_buffer.is_empty() {
|
||||
return Ok(());
|
||||
pub(crate) async fn write_buffer_flush(&mut self) -> Result<(), std::io::Error> {
|
||||
for (_, state) in self.states.clone() {
|
||||
let writer_buffer = state.write_buffer.clone();
|
||||
if writer_buffer.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.psh(&writer_buffer, state.host_port).await.ok(); // don't care
|
||||
|
||||
// we have to borrow mutably after self.psh
|
||||
if let Some(state) = self.states.get_mut(&state.host_port) {
|
||||
state.write_buffer.clear();
|
||||
}
|
||||
}
|
||||
trace!("Flushing {} bytes", self.write_buffer.len());
|
||||
let write_buffer = self.write_buffer.clone();
|
||||
self.psh(&write_buffer).await?;
|
||||
self.write_buffer = Vec::new();
|
||||
|
||||
// Since we have extra clocks and we haven't been cancelled by the runtime, let's reap the
|
||||
// dropped connections
|
||||
for d in self.dropped.clone() {
|
||||
if let Some(state) = self.states.remove(&d) {
|
||||
self.close(state.host_port).await.ok();
|
||||
}
|
||||
}
|
||||
// We can't clear until it's all done, since we can get cancelled by the runtime at any
|
||||
// point.
|
||||
self.dropped.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn queue_send(
|
||||
&mut self,
|
||||
payload: &[u8],
|
||||
host_port: u16,
|
||||
) -> Result<(), std::io::Error> {
|
||||
if let Some(state) = self.states.get_mut(&host_port) {
|
||||
state.write_buffer.extend_from_slice(payload);
|
||||
} else {
|
||||
return Err(std::io::Error::new(
|
||||
ErrorKind::NotConnected,
|
||||
"not connected",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn uncache(
|
||||
&mut self,
|
||||
to_copy: usize,
|
||||
host_port: u16,
|
||||
) -> Result<Vec<u8>, std::io::Error> {
|
||||
if let Some(state) = self.states.get_mut(&host_port) {
|
||||
let to_copy = if to_copy > state.read_buffer.len() {
|
||||
state.read_buffer.len()
|
||||
} else {
|
||||
to_copy
|
||||
};
|
||||
|
||||
let res = state.read_buffer[..to_copy].to_vec();
|
||||
state.read_buffer = state.read_buffer[to_copy..].to_vec();
|
||||
Ok(res)
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
ErrorKind::NotConnected,
|
||||
"not connected",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn cache_read(
|
||||
&mut self,
|
||||
payload: &[u8],
|
||||
host_port: u16,
|
||||
) -> Result<(), std::io::Error> {
|
||||
if let Some(state) = self.states.get_mut(&host_port) {
|
||||
state.read_buffer.extend_from_slice(payload);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
ErrorKind::NotConnected,
|
||||
"not connected",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_status(&self, host_port: u16) -> Result<ConnectionStatus, std::io::Error> {
|
||||
if let Some(state) = self.states.get(&host_port) {
|
||||
Ok(state.status.clone())
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
ErrorKind::NotConnected,
|
||||
"not connected",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives data from the connection.
|
||||
///
|
||||
/// # Returns
|
||||
@@ -362,31 +469,26 @@ impl Adapter {
|
||||
/// # Errors
|
||||
/// * Returns `ConnectionReset` if connection was reset or closed
|
||||
/// * Returns other IO errors if underlying transport fails
|
||||
pub async fn recv(&mut self) -> Result<Vec<u8>, std::io::Error> {
|
||||
pub(crate) async fn recv(&mut self, host_port: u16) -> Result<Vec<u8>, std::io::Error> {
|
||||
loop {
|
||||
let res = self.read_tcp_packet().await?;
|
||||
if res.destination_port != self.host_port || res.source_port != self.peer_port {
|
||||
continue;
|
||||
}
|
||||
if res.flags.psh || !res.payload.is_empty() {
|
||||
self.ack().await?;
|
||||
break Ok(res.payload);
|
||||
}
|
||||
if res.flags.rst {
|
||||
self.state = AdapterState::None;
|
||||
break Err(std::io::Error::new(
|
||||
std::io::ErrorKind::ConnectionReset,
|
||||
"Connection reset",
|
||||
));
|
||||
}
|
||||
if res.flags.fin {
|
||||
self.ack().await?;
|
||||
self.state = AdapterState::None;
|
||||
break Err(std::io::Error::new(
|
||||
std::io::ErrorKind::ConnectionReset,
|
||||
"Connection reset",
|
||||
// Check to see if we already have some cached
|
||||
if let Some(state) = self.states.get_mut(&host_port) {
|
||||
if !state.read_buffer.is_empty() {
|
||||
let res = state.read_buffer.clone();
|
||||
state.read_buffer = Vec::new();
|
||||
return Ok(res);
|
||||
}
|
||||
if let ConnectionStatus::Error(e) = state.status {
|
||||
return Err(std::io::Error::new(e, "socket io error"));
|
||||
}
|
||||
} else {
|
||||
return Err(std::io::Error::new(
|
||||
ErrorKind::NotConnected,
|
||||
"not connected",
|
||||
));
|
||||
}
|
||||
|
||||
self.process_tcp_packet().await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,24 +515,40 @@ impl Adapter {
|
||||
})
|
||||
}
|
||||
|
||||
async fn read_tcp_packet(&mut self) -> Result<TcpPacket, std::io::Error> {
|
||||
loop {
|
||||
let ip_packet = self.read_ip_packet().await?;
|
||||
let tcp_packet = TcpPacket::parse(&ip_packet)?;
|
||||
if tcp_packet.destination_port != self.host_port
|
||||
|| tcp_packet.source_port != self.peer_port
|
||||
{
|
||||
continue;
|
||||
}
|
||||
trace!("TCP packet: {tcp_packet:#?}");
|
||||
self.ack = tcp_packet.sequence_number
|
||||
+ if tcp_packet.payload.is_empty() {
|
||||
async fn process_tcp_packet(&mut self) -> Result<(), std::io::Error> {
|
||||
let ip_packet = self.read_ip_packet().await?;
|
||||
let res = TcpPacket::parse(&ip_packet)?;
|
||||
let mut ack_me = None;
|
||||
if let Some(state) = self.states.get_mut(&res.destination_port) {
|
||||
state.ack = res.sequence_number
|
||||
+ if res.payload.is_empty() {
|
||||
1
|
||||
} else {
|
||||
tcp_packet.payload.len() as u32
|
||||
res.payload.len() as u32
|
||||
};
|
||||
break Ok(tcp_packet);
|
||||
if res.flags.psh || !res.payload.is_empty() {
|
||||
ack_me = Some(res.destination_port);
|
||||
state.read_buffer.extend(res.payload)
|
||||
}
|
||||
if res.flags.rst {
|
||||
state.status = ConnectionStatus::Error(ErrorKind::ConnectionReset);
|
||||
}
|
||||
if res.flags.fin {
|
||||
ack_me = Some(res.destination_port);
|
||||
state.status = ConnectionStatus::Error(ErrorKind::ConnectionReset);
|
||||
}
|
||||
if res.flags.syn && res.flags.ack {
|
||||
ack_me = Some(res.destination_port);
|
||||
state.seq = state.seq.wrapping_add(1);
|
||||
state.status = ConnectionStatus::Connected;
|
||||
}
|
||||
}
|
||||
|
||||
// we have to ack outside of the mutable state borrow
|
||||
if let Some(a) = ack_me {
|
||||
self.ack(a).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ip_wrap(&self, packet: &[u8]) -> Vec<u8> {
|
||||
@@ -454,125 +572,3 @@ impl Adapter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for Adapter {
|
||||
/// Attempts to read from the connection into the provided buffer.
|
||||
///
|
||||
/// Uses an internal read buffer to cache any extra received data.
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Poll::Ready(Ok(()))` if data was read successfully
|
||||
/// * `Poll::Ready(Err(e))` if an error occurred
|
||||
/// * `Poll::Pending` if operation would block
|
||||
///
|
||||
/// # Errors
|
||||
/// * Returns `NotConnected` if adapter isn't connected
|
||||
/// * Propagates any underlying transport errors
|
||||
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<()>> {
|
||||
// First, check if we have any cached data
|
||||
if !self.read_buffer.is_empty() {
|
||||
let to_copy = std::cmp::min(buf.remaining(), self.read_buffer.len());
|
||||
buf.put_slice(&self.read_buffer[..to_copy]);
|
||||
|
||||
// Keep any remaining data in the buffer
|
||||
if to_copy < self.read_buffer.len() {
|
||||
self.read_buffer = self.read_buffer[to_copy..].to_vec();
|
||||
} else {
|
||||
self.read_buffer.clear();
|
||||
}
|
||||
|
||||
return std::task::Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
// If no cached data and not connected, return error
|
||||
if self.state != AdapterState::Connected {
|
||||
return std::task::Poll::Ready(Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotConnected,
|
||||
"Adapter not connected",
|
||||
)));
|
||||
}
|
||||
|
||||
// If no cached data, try to receive new data
|
||||
let future = async {
|
||||
match self.recv().await {
|
||||
Ok(data) => {
|
||||
let len = std::cmp::min(buf.remaining(), data.len());
|
||||
buf.put_slice(&data[..len]);
|
||||
|
||||
// If we received more data than needed, cache the rest
|
||||
if len < data.len() {
|
||||
self.read_buffer = data[len..].to_vec();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
};
|
||||
|
||||
// Pin the future and poll it
|
||||
futures::pin_mut!(future);
|
||||
future.poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Adapter {
|
||||
/// Attempts to write data to the connection.
|
||||
///
|
||||
/// Data is buffered internally until flushed.
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Poll::Ready(Ok(n))` with number of bytes written
|
||||
/// * `Poll::Ready(Err(e))` if an error occurred
|
||||
/// * `Poll::Pending` if operation would block
|
||||
///
|
||||
/// # Errors
|
||||
/// * Returns `NotConnected` if adapter isn't connected
|
||||
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>> {
|
||||
trace!("poll psh {}", buf.len());
|
||||
if self.state != AdapterState::Connected {
|
||||
return std::task::Poll::Ready(Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotConnected,
|
||||
"Adapter not connected",
|
||||
)));
|
||||
}
|
||||
self.write_buffer.extend_from_slice(buf);
|
||||
Poll::Ready(Ok(buf.len()))
|
||||
}
|
||||
|
||||
fn poll_flush(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
let future = async {
|
||||
match self.write_buffer_flush().await {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
};
|
||||
|
||||
// Pin the future and poll it
|
||||
futures::pin_mut!(future);
|
||||
future.poll(cx)
|
||||
}
|
||||
|
||||
fn poll_shutdown(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
// Create a future that can be polled
|
||||
let future = async { self.close().await };
|
||||
|
||||
// Pin the future and poll it
|
||||
futures::pin_mut!(future);
|
||||
future.poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,16 @@ use std::{
|
||||
};
|
||||
|
||||
use log::debug;
|
||||
use stream::AdapterStream;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
use crate::provider::RsdProvider;
|
||||
|
||||
pub mod adapter;
|
||||
pub mod packets;
|
||||
pub mod stream;
|
||||
|
||||
pub(crate) async fn log_packet(file: &Arc<tokio::sync::Mutex<tokio::fs::File>>, packet: &[u8]) {
|
||||
pub(crate) fn log_packet(file: &Arc<tokio::sync::Mutex<tokio::fs::File>>, packet: &[u8]) {
|
||||
debug!("Logging {} byte packet", packet.len());
|
||||
let packet = packet.to_vec();
|
||||
let file = file.to_owned();
|
||||
@@ -35,6 +39,18 @@ pub(crate) async fn log_packet(file: &Arc<tokio::sync::Mutex<tokio::fs::File>>,
|
||||
});
|
||||
}
|
||||
|
||||
impl<'a> RsdProvider<'a> for adapter::Adapter {
|
||||
async fn connect_to_service_port(
|
||||
&'a mut self,
|
||||
port: u16,
|
||||
) -> Result<stream::AdapterStream<'a>, crate::IdeviceError> {
|
||||
let s = stream::AdapterStream::connect(self, port).await?;
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
type Stream = AdapterStream<'a>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
@@ -49,6 +65,7 @@ mod tests {
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use stream::AdapterStream;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tun_rs::DeviceBuilder;
|
||||
|
||||
@@ -186,31 +203,35 @@ mod tests {
|
||||
let mut buf = Vec::new();
|
||||
let _ = tokio::io::stdin().read(&mut buf).await.unwrap();
|
||||
|
||||
if let Err(e) = adapter.connect(SERVER_PORT).await {
|
||||
println!("no connect: {e:?}");
|
||||
}
|
||||
let mut stream = match AdapterStream::connect(&mut adapter, SERVER_PORT).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
println!("no connect: {e:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = adapter.write_all(&[1, 2, 3, 4, 5]).await {
|
||||
if let Err(e) = stream.write_all(&[1, 2, 3, 4, 5]).await {
|
||||
println!("no send: {e:?}");
|
||||
} else {
|
||||
let mut buf = [0u8; 4];
|
||||
match adapter.read_exact(&mut buf).await {
|
||||
match stream.read_exact(&mut buf).await {
|
||||
Ok(_) => println!("recv'd {buf:?}"),
|
||||
Err(e) => println!("no recv: {e:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = adapter.write_all(&[69, 69, 42, 0, 1]).await {
|
||||
if let Err(e) = stream.write_all(&[69, 69, 42, 0, 1]).await {
|
||||
println!("no send: {e:?}");
|
||||
} else {
|
||||
let mut buf = [0u8; 6];
|
||||
match adapter.read_exact(&mut buf).await {
|
||||
match stream.read_exact(&mut buf).await {
|
||||
Ok(_) => println!("recv'd {buf:?}"),
|
||||
Err(e) => println!("no recv: {e:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = adapter.close().await {
|
||||
if let Err(e) = stream.close().await {
|
||||
println!("no close: {e:?}");
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ impl Ipv4Packet {
|
||||
reader.read_exact(&mut payload).await?;
|
||||
if let Some(log) = log {
|
||||
log_packet.extend_from_slice(&payload);
|
||||
super::log_packet(log, &log_packet).await;
|
||||
super::log_packet(log, &log_packet);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
@@ -324,7 +324,7 @@ impl Ipv6Packet {
|
||||
reader.read_exact(&mut payload).await?;
|
||||
if let Some(log) = log {
|
||||
log_packet.extend_from_slice(&payload);
|
||||
super::log_packet(log, &log_packet).await;
|
||||
super::log_packet(log, &log_packet);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
|
||||
177
idevice/src/tcp/stream.rs
Normal file
177
idevice/src/tcp/stream.rs
Normal file
@@ -0,0 +1,177 @@
|
||||
//! A stream for the adapter
|
||||
|
||||
use std::{future::Future, task::Poll};
|
||||
|
||||
use log::trace;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use crate::tcp::adapter::ConnectionStatus;
|
||||
|
||||
use super::adapter::Adapter;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AdapterStream<'a> {
|
||||
pub(crate) adapter: &'a mut Adapter,
|
||||
pub host_port: u16,
|
||||
pub peer_port: u16,
|
||||
}
|
||||
|
||||
impl<'a> AdapterStream<'a> {
|
||||
/// Connect to the specified port
|
||||
pub async fn connect(adapter: &'a mut Adapter, port: u16) -> Result<Self, std::io::Error> {
|
||||
let host_port = adapter.connect(port).await?;
|
||||
Ok(Self {
|
||||
adapter,
|
||||
host_port,
|
||||
peer_port: port,
|
||||
})
|
||||
}
|
||||
|
||||
/// Gracefully closes the stream
|
||||
pub async fn close(&mut self) -> Result<(), std::io::Error> {
|
||||
self.adapter.close(self.host_port).await
|
||||
}
|
||||
|
||||
/// Sends data to the target
|
||||
pub async fn psh(&mut self, payload: &[u8]) -> Result<(), std::io::Error> {
|
||||
self.adapter.queue_send(payload, self.host_port)?;
|
||||
self.adapter.write_buffer_flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn recv(&mut self) -> Result<Vec<u8>, std::io::Error> {
|
||||
self.adapter.recv(self.host_port).await
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for AdapterStream<'_> {
|
||||
/// Attempts to read from the connection into the provided buffer.
|
||||
///
|
||||
/// Uses an internal read buffer to cache any extra received data.
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Poll::Ready(Ok(()))` if data was read successfully
|
||||
/// * `Poll::Ready(Err(e))` if an error occurred
|
||||
/// * `Poll::Pending` if operation would block
|
||||
///
|
||||
/// # Errors
|
||||
/// * Returns `NotConnected` if adapter isn't connected
|
||||
/// * Propagates any underlying transport errors
|
||||
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<()>> {
|
||||
match self.adapter.get_status(self.host_port) {
|
||||
Ok(ConnectionStatus::Error(e)) => {
|
||||
return std::task::Poll::Ready(Err(std::io::Error::new(e, "io error")));
|
||||
}
|
||||
Err(e) => {
|
||||
return std::task::Poll::Ready(Err(e));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// First, check if we have any cached data
|
||||
let p = self.host_port;
|
||||
let cache = match self.adapter.uncache(buf.remaining(), p) {
|
||||
Ok(c) => c,
|
||||
Err(e) => return std::task::Poll::Ready(Err(e)),
|
||||
};
|
||||
if !cache.is_empty() {
|
||||
buf.put_slice(&cache);
|
||||
return std::task::Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
// If no cached data, try to receive new data
|
||||
let future = async {
|
||||
match self.adapter.recv(p).await {
|
||||
Ok(data) => {
|
||||
let len = std::cmp::min(buf.remaining(), data.len());
|
||||
buf.put_slice(&data[..len]);
|
||||
|
||||
// If we received more data than needed, cache the rest
|
||||
if len < data.len() {
|
||||
self.adapter.cache_read(&data[len..], p)?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
};
|
||||
|
||||
// Pin the future and poll it
|
||||
futures::pin_mut!(future);
|
||||
future.poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for AdapterStream<'_> {
|
||||
/// Attempts to write data to the connection.
|
||||
///
|
||||
/// Data is buffered internally until flushed.
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Poll::Ready(Ok(n))` with number of bytes written
|
||||
/// * `Poll::Ready(Err(e))` if an error occurred
|
||||
/// * `Poll::Pending` if operation would block
|
||||
///
|
||||
/// # Errors
|
||||
/// * Returns `NotConnected` if adapter isn't connected
|
||||
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>> {
|
||||
trace!("poll psh {}", buf.len());
|
||||
match self.adapter.get_status(self.host_port) {
|
||||
Ok(ConnectionStatus::Error(e)) => {
|
||||
return std::task::Poll::Ready(Err(std::io::Error::new(e, "io error")));
|
||||
}
|
||||
Err(e) => {
|
||||
return std::task::Poll::Ready(Err(e));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let p = self.host_port;
|
||||
match self.adapter.queue_send(buf, p) {
|
||||
Ok(_) => Poll::Ready(Ok(buf.len())),
|
||||
Err(e) => Poll::Ready(Err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_flush(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
let future = async {
|
||||
match self.adapter.write_buffer_flush().await {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
};
|
||||
|
||||
// Pin the future and poll it
|
||||
futures::pin_mut!(future);
|
||||
future.poll(cx)
|
||||
}
|
||||
|
||||
fn poll_shutdown(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
// Create a future that can be polled
|
||||
let future = async { self.close().await };
|
||||
|
||||
// Pin the future and poll it
|
||||
futures::pin_mut!(future);
|
||||
future.poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AdapterStream<'_> {
|
||||
fn drop(&mut self) {
|
||||
self.adapter.connection_drop(self.host_port);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
// DebianArch
|
||||
|
||||
use std::{
|
||||
ffi::CString,
|
||||
io::{BufRead, Cursor, Read},
|
||||
ops::{BitOr, BitOrAssign},
|
||||
};
|
||||
|
||||
use super::error::XPCError;
|
||||
use indexmap::IndexMap;
|
||||
use log::debug;
|
||||
use log::{debug, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::IdeviceError;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum XPCFlag {
|
||||
@@ -69,7 +68,7 @@ pub enum XPCType {
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for XPCType {
|
||||
type Error = XPCError;
|
||||
type Error = IdeviceError;
|
||||
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
@@ -81,7 +80,7 @@ impl TryFrom<u32> for XPCType {
|
||||
0x00009000 => Ok(Self::String),
|
||||
0x00008000 => Ok(Self::Data),
|
||||
0x0000a000 => Ok(Self::Uuid),
|
||||
_ => Err("Invalid XPCType")?,
|
||||
_ => Err(IdeviceError::UnknownXpcType(value))?,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,14 +146,7 @@ impl XPCObject {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_value<T: Serialize>(value: &T) -> Self {
|
||||
match plist::to_value(value) {
|
||||
Ok(v) => Self::from(v),
|
||||
Err(_) => panic!("oof"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode(&self) -> Result<Vec<u8>, XPCError> {
|
||||
pub fn encode(&self) -> Result<Vec<u8>, IdeviceError> {
|
||||
let mut buf = Vec::new();
|
||||
buf.extend_from_slice(&0x42133742_u32.to_le_bytes());
|
||||
buf.extend_from_slice(&0x00000005_u32.to_le_bytes());
|
||||
@@ -162,7 +154,7 @@ impl XPCObject {
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn encode_object(&self, buf: &mut Vec<u8>) -> Result<(), XPCError> {
|
||||
fn encode_object(&self, buf: &mut Vec<u8>) -> Result<(), IdeviceError> {
|
||||
match self {
|
||||
XPCObject::Bool(val) => {
|
||||
buf.extend_from_slice(&(XPCType::Bool as u32).to_le_bytes());
|
||||
@@ -224,21 +216,26 @@ impl XPCObject {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn decode(buf: &[u8]) -> Result<Self, XPCError> {
|
||||
let magic = u32::from_le_bytes(buf[0..4].try_into()?);
|
||||
pub fn decode(buf: &[u8]) -> Result<Self, IdeviceError> {
|
||||
if buf.len() < 8 {
|
||||
return Err(IdeviceError::NotEnoughBytes(buf.len(), 8));
|
||||
}
|
||||
let magic = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
|
||||
if magic != 0x42133742 {
|
||||
Err("Invalid magic for XPCObject")?
|
||||
warn!("Invalid magic for XPCObject");
|
||||
return Err(IdeviceError::InvalidXpcMagic);
|
||||
}
|
||||
|
||||
let version = u32::from_le_bytes(buf[4..8].try_into()?);
|
||||
let version = u32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]);
|
||||
if version != 0x00000005 {
|
||||
Err("Unexpected version for XPCObject")?
|
||||
warn!("Unexpected version for XPCObject");
|
||||
return Err(IdeviceError::UnexpectedXpcVersion);
|
||||
}
|
||||
|
||||
Self::decode_object(&mut Cursor::new(&buf[8..]))
|
||||
}
|
||||
|
||||
fn decode_object(mut cursor: &mut Cursor<&[u8]>) -> Result<Self, XPCError> {
|
||||
fn decode_object(mut cursor: &mut Cursor<&[u8]>) -> Result<Self, IdeviceError> {
|
||||
let mut buf_32: [u8; 4] = Default::default();
|
||||
cursor.read_exact(&mut buf_32)?;
|
||||
let xpc_type = u32::from_le_bytes(buf_32);
|
||||
@@ -251,10 +248,18 @@ impl XPCObject {
|
||||
let _l = u32::from_le_bytes(buf_32);
|
||||
cursor.read_exact(&mut buf_32)?;
|
||||
let num_entries = u32::from_le_bytes(buf_32);
|
||||
for _i in 0..num_entries {
|
||||
for _ in 0..num_entries {
|
||||
let mut key_buf = Vec::new();
|
||||
BufRead::read_until(&mut cursor, 0, &mut key_buf)?;
|
||||
let key = CString::from_vec_with_nul(key_buf)?.to_str()?.to_string();
|
||||
let key = match CString::from_vec_with_nul(key_buf)
|
||||
.ok()
|
||||
.and_then(|x| x.to_str().ok().map(|x| x.to_string()))
|
||||
{
|
||||
Some(k) => k,
|
||||
None => {
|
||||
return Err(IdeviceError::InvalidCString);
|
||||
}
|
||||
};
|
||||
let padding = Self::calculate_padding(key.len() + 1);
|
||||
|
||||
BufRead::consume(&mut cursor, padding);
|
||||
@@ -292,7 +297,13 @@ impl XPCObject {
|
||||
|
||||
let mut key_buf = vec![0; l];
|
||||
cursor.read_exact(&mut key_buf)?;
|
||||
let key = CString::from_vec_with_nul(key_buf)?.to_str()?.to_string();
|
||||
let key = match CString::from_vec_with_nul(key_buf)
|
||||
.ok()
|
||||
.and_then(|x| x.to_str().ok().map(|x| x.to_string()))
|
||||
{
|
||||
Some(k) => k,
|
||||
None => return Err(IdeviceError::InvalidCString),
|
||||
};
|
||||
BufRead::consume(&mut cursor, padding);
|
||||
Ok(XPCObject::String(key))
|
||||
}
|
||||
@@ -395,40 +406,48 @@ impl XPCMessage {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode(data: &[u8]) -> Result<XPCMessage, XPCError> {
|
||||
pub fn decode(data: &[u8]) -> Result<XPCMessage, IdeviceError> {
|
||||
if data.len() < 24 {
|
||||
Err("XPCMessage must be at least 24 bytes.")?
|
||||
Err(IdeviceError::NotEnoughBytes(data.len(), 24))?
|
||||
}
|
||||
|
||||
let magic = u32::from_le_bytes(data[0..4].try_into()?);
|
||||
let magic = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
|
||||
if magic != 0x29b00b92_u32 {
|
||||
Err("XPCMessage magic is invalid.")?
|
||||
warn!("XPCMessage magic is invalid.");
|
||||
Err(IdeviceError::MalformedXpc)?
|
||||
}
|
||||
|
||||
let flags = u32::from_le_bytes(data[4..8].try_into()?);
|
||||
let body_len = u64::from_le_bytes(data[8..16].try_into()?);
|
||||
let message_id = u64::from_le_bytes(data[16..24].try_into()?);
|
||||
if body_len + 24 > data.len().try_into()? {
|
||||
Err("XPCMessage body length given is incorrect.")?
|
||||
let flags = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
|
||||
let body_len = u64::from_le_bytes([
|
||||
data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
|
||||
]);
|
||||
debug!("Body_len: {body_len}");
|
||||
let message_id = u64::from_le_bytes([
|
||||
data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23],
|
||||
]);
|
||||
if body_len + 24 > data.len() as u64 {
|
||||
warn!(
|
||||
"Body length is {body_len}, but received bytes is {}",
|
||||
data.len()
|
||||
);
|
||||
Err(IdeviceError::PacketSizeMismatch)?
|
||||
}
|
||||
|
||||
// for some reason the above if check doesn't work ???
|
||||
debug!("Body length {} : {}", body_len, data.len());
|
||||
if body_len == 0 {
|
||||
return Ok(XPCMessage {
|
||||
flags,
|
||||
message: None,
|
||||
message_id: Some(message_id),
|
||||
});
|
||||
}
|
||||
Ok(XPCMessage {
|
||||
let res = XPCMessage {
|
||||
flags,
|
||||
message: Some(XPCObject::decode(&data[24..24 + body_len as usize])?),
|
||||
message: if body_len > 0 {
|
||||
Some(XPCObject::decode(&data[24..24 + body_len as usize])?)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
message_id: Some(message_id),
|
||||
})
|
||||
};
|
||||
|
||||
debug!("Decoded {res:#?}");
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn encode(self, message_id: u64) -> Result<Vec<u8>, XPCError> {
|
||||
pub fn encode(self, message_id: u64) -> Result<Vec<u8>, IdeviceError> {
|
||||
let mut out = 0x29b00b92_u32.to_le_bytes().to_vec();
|
||||
out.extend_from_slice(&self.flags.to_le_bytes());
|
||||
match self.message {
|
||||
196
idevice/src/xpc/http2/frame.rs
Normal file
196
idevice/src/xpc/http2/frame.rs
Normal file
@@ -0,0 +1,196 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use crate::{IdeviceError, ReadWrite};
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
pub trait HttpFrame {
|
||||
fn serialize(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)] // we don't care about frames from the device
|
||||
pub enum Frame {
|
||||
Settings(SettingsFrame),
|
||||
WindowUpdate(WindowUpdateFrame),
|
||||
Headers(HeadersFrame),
|
||||
Data(DataFrame),
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
pub async fn next(mut socket: &mut impl ReadWrite) -> Result<Self, IdeviceError> {
|
||||
// Read the len of the frame
|
||||
let mut buf = [0u8; 3];
|
||||
tokio::io::AsyncReadExt::read_exact(&mut socket, &mut buf).await?;
|
||||
let frame_len = u32::from_be_bytes([0x00, buf[0], buf[1], buf[2]]);
|
||||
|
||||
// Read the fields
|
||||
let frame_type = socket.read_u8().await?;
|
||||
let flags = socket.read_u8().await?;
|
||||
let stream_id = socket.read_u32().await?;
|
||||
|
||||
let mut body = vec![0; frame_len as usize];
|
||||
socket.read_exact(&mut body).await?;
|
||||
|
||||
Ok(match frame_type {
|
||||
0x00 => {
|
||||
// data
|
||||
Self::Data(DataFrame {
|
||||
stream_id,
|
||||
payload: body,
|
||||
})
|
||||
}
|
||||
0x01 => {
|
||||
// headers
|
||||
Self::Headers(HeadersFrame { stream_id })
|
||||
}
|
||||
0x03 => return Err(IdeviceError::HttpStreamReset),
|
||||
0x04 => {
|
||||
// settings
|
||||
let mut body = std::io::Cursor::new(body);
|
||||
let mut settings = Vec::new();
|
||||
|
||||
while let Ok(setting_type) = body.read_u16().await {
|
||||
settings.push(match setting_type {
|
||||
0x03 => {
|
||||
let max_streams = body.read_u32().await?;
|
||||
Setting::MaxConcurrentStreams(max_streams)
|
||||
}
|
||||
0x04 => {
|
||||
let window_size = body.read_u32().await?;
|
||||
Setting::InitialWindowSize(window_size)
|
||||
}
|
||||
_ => {
|
||||
return Err(IdeviceError::UnknownHttpSetting(setting_type));
|
||||
}
|
||||
});
|
||||
}
|
||||
Self::Settings(SettingsFrame {
|
||||
settings,
|
||||
stream_id,
|
||||
flags,
|
||||
})
|
||||
}
|
||||
0x07 => {
|
||||
let msg = if body.len() < 8 {
|
||||
"<MISSING>".to_string()
|
||||
} else {
|
||||
String::from_utf8_lossy(&body[8..]).to_string()
|
||||
};
|
||||
return Err(IdeviceError::HttpGoAway(msg));
|
||||
}
|
||||
0x08 => {
|
||||
// window update
|
||||
if body.len() != 4 {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
|
||||
let window = u32::from_be_bytes([body[0], body[1], body[2], body[3]]);
|
||||
Self::WindowUpdate(WindowUpdateFrame {
|
||||
increment_size: window,
|
||||
stream_id,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
return Err(IdeviceError::UnknownFrame(frame_type));
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SettingsFrame {
|
||||
pub settings: Vec<Setting>,
|
||||
pub stream_id: u32,
|
||||
pub flags: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Setting {
|
||||
MaxConcurrentStreams(u32),
|
||||
InitialWindowSize(u32),
|
||||
}
|
||||
|
||||
impl Setting {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Setting::MaxConcurrentStreams(m) => {
|
||||
let mut res = vec![0x00, 0x03];
|
||||
res.extend(m.to_be_bytes());
|
||||
res
|
||||
}
|
||||
Setting::InitialWindowSize(s) => {
|
||||
let mut res = vec![0x00, 0x04];
|
||||
res.extend(s.to_be_bytes());
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpFrame for SettingsFrame {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let settings = self
|
||||
.settings
|
||||
.iter()
|
||||
.map(|x| x.serialize())
|
||||
.collect::<Vec<Vec<u8>>>()
|
||||
.concat();
|
||||
let settings_len = (settings.len() as u32).to_be_bytes();
|
||||
let mut res = vec![
|
||||
settings_len[1],
|
||||
settings_len[2],
|
||||
settings_len[3],
|
||||
0x04,
|
||||
self.flags,
|
||||
];
|
||||
res.extend(self.stream_id.to_be_bytes());
|
||||
res.extend(settings);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WindowUpdateFrame {
|
||||
pub increment_size: u32,
|
||||
pub stream_id: u32,
|
||||
}
|
||||
|
||||
impl HttpFrame for WindowUpdateFrame {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut res = vec![0x00, 0x00, 0x04, 0x08, 0x00]; // size, frame ID, flags
|
||||
res.extend(self.stream_id.to_be_bytes());
|
||||
res.extend(self.increment_size.to_be_bytes());
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// We don't actually care about this frame according to spec. This is just to open new channels.
|
||||
pub struct HeadersFrame {
|
||||
pub stream_id: u32,
|
||||
}
|
||||
|
||||
impl HttpFrame for HeadersFrame {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut res = vec![0x00, 0x00, 0x00, 0x01, 0x04];
|
||||
res.extend(self.stream_id.to_be_bytes());
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DataFrame {
|
||||
pub stream_id: u32,
|
||||
pub payload: Vec<u8>,
|
||||
}
|
||||
|
||||
impl HttpFrame for DataFrame {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut res = (self.payload.len() as u32).to_be_bytes().to_vec();
|
||||
res.remove(0); // only 3 significant bytes
|
||||
res.extend([0x00, 0x00]); // frame type, flags
|
||||
res.extend(self.stream_id.to_be_bytes());
|
||||
res.extend(self.payload.clone());
|
||||
res
|
||||
}
|
||||
}
|
||||
136
idevice/src/xpc/http2/mod.rs
Normal file
136
idevice/src/xpc/http2/mod.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use frame::HttpFrame;
|
||||
use log::{debug, warn};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
use crate::{IdeviceError, ReadWrite};
|
||||
|
||||
pub mod frame;
|
||||
pub use frame::Setting;
|
||||
|
||||
const HTTP2_MAGIC: &[u8] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".as_bytes();
|
||||
|
||||
pub struct Http2Client<R: ReadWrite> {
|
||||
inner: R,
|
||||
cache: HashMap<u32, VecDeque<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl<R: ReadWrite> Http2Client<R> {
|
||||
/// Writes the magic and inits the caches
|
||||
pub async fn new(mut inner: R) -> Result<Self, IdeviceError> {
|
||||
inner.write_all(HTTP2_MAGIC).await?;
|
||||
inner.flush().await?;
|
||||
Ok(Self {
|
||||
inner,
|
||||
cache: HashMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn set_settings(
|
||||
&mut self,
|
||||
settings: Vec<frame::Setting>,
|
||||
stream_id: u32,
|
||||
) -> Result<(), IdeviceError> {
|
||||
let frame = frame::SettingsFrame {
|
||||
settings,
|
||||
stream_id,
|
||||
flags: 0,
|
||||
}
|
||||
.serialize();
|
||||
self.inner.write_all(&frame).await?;
|
||||
self.inner.flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn window_update(
|
||||
&mut self,
|
||||
increment_size: u32,
|
||||
stream_id: u32,
|
||||
) -> Result<(), IdeviceError> {
|
||||
let frame = frame::WindowUpdateFrame {
|
||||
increment_size,
|
||||
stream_id,
|
||||
}
|
||||
.serialize();
|
||||
self.inner.write_all(&frame).await?;
|
||||
self.inner.flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn open_stream(&mut self, stream_id: u32) -> Result<(), IdeviceError> {
|
||||
self.cache.insert(stream_id, VecDeque::new());
|
||||
let frame = frame::HeadersFrame { stream_id }.serialize();
|
||||
self.inner.write_all(&frame).await?;
|
||||
self.inner.flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send(&mut self, payload: Vec<u8>, stream_id: u32) -> Result<(), IdeviceError> {
|
||||
let frame = frame::DataFrame { stream_id, payload }.serialize();
|
||||
self.inner.write_all(&frame).await?;
|
||||
self.inner.flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read(&mut self, stream_id: u32) -> Result<Vec<u8>, IdeviceError> {
|
||||
// See if we already have a cached message from another read
|
||||
match self.cache.get_mut(&stream_id) {
|
||||
Some(c) => {
|
||||
if let Some(d) = c.pop_front() {
|
||||
return Ok(d);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.cache.insert(stream_id, VecDeque::new());
|
||||
}
|
||||
};
|
||||
|
||||
// handle packets until we get what we want
|
||||
loop {
|
||||
let frame = frame::Frame::next(&mut self.inner).await?;
|
||||
// debug!("Got frame: {frame:#?}");
|
||||
match frame {
|
||||
frame::Frame::Settings(settings_frame) => {
|
||||
if settings_frame.flags != 1 {
|
||||
// ack that
|
||||
let frame = frame::SettingsFrame {
|
||||
settings: Vec::new(),
|
||||
stream_id: settings_frame.stream_id,
|
||||
flags: 1,
|
||||
}
|
||||
.serialize();
|
||||
self.inner.write_all(&frame).await?;
|
||||
self.inner.flush().await?;
|
||||
}
|
||||
}
|
||||
frame::Frame::Data(data_frame) => {
|
||||
debug!(
|
||||
"Got data frame for {} with {} bytes",
|
||||
data_frame.stream_id,
|
||||
data_frame.payload.len()
|
||||
);
|
||||
if data_frame.stream_id == stream_id {
|
||||
return Ok(data_frame.payload);
|
||||
} else {
|
||||
let c = match self.cache.get_mut(&data_frame.stream_id) {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
warn!(
|
||||
"Received message for stream ID {} not in cache",
|
||||
data_frame.stream_id
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
c.push_back(data_frame.payload);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// do nothing, we shouldn't receive these frames
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
130
idevice/src/xpc/mod.rs
Normal file
130
idevice/src/xpc/mod.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use http2::Setting;
|
||||
use log::debug;
|
||||
|
||||
use crate::{IdeviceError, ReadWrite};
|
||||
|
||||
mod format;
|
||||
mod http2;
|
||||
|
||||
pub use format::XPCMessage;
|
||||
use format::{XPCFlag, XPCObject};
|
||||
|
||||
const ROOT_CHANNEL: u32 = 1;
|
||||
const REPLY_CHANNEL: u32 = 3;
|
||||
|
||||
pub struct RemoteXpcClient<R: ReadWrite> {
|
||||
h2_client: http2::Http2Client<R>,
|
||||
root_id: u64,
|
||||
reply_id: u64,
|
||||
}
|
||||
|
||||
impl<R: ReadWrite> RemoteXpcClient<R> {
|
||||
pub async fn new(socket: R) -> Result<Self, IdeviceError> {
|
||||
Ok(Self {
|
||||
h2_client: http2::Http2Client::new(socket).await?,
|
||||
root_id: 1,
|
||||
reply_id: 1,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn do_handshake(&mut self) -> Result<plist::Value, IdeviceError> {
|
||||
self.h2_client
|
||||
.set_settings(
|
||||
vec![
|
||||
Setting::MaxConcurrentStreams(10),
|
||||
Setting::InitialWindowSize(1048576),
|
||||
],
|
||||
0,
|
||||
)
|
||||
.await?;
|
||||
self.h2_client.window_update(983041, 0).await?;
|
||||
self.h2_client.open_stream(1).await?; // root channel
|
||||
|
||||
debug!("Sending empty dictionary");
|
||||
self.send_root(XPCMessage::new(
|
||||
Some(XPCFlag::AlwaysSet),
|
||||
Some(XPCObject::Dictionary(Default::default())),
|
||||
None,
|
||||
))
|
||||
.await?;
|
||||
|
||||
self.recv_root().await?;
|
||||
self.recv_root().await?;
|
||||
|
||||
debug!("Sending weird flags");
|
||||
self.send_root(XPCMessage::new(Some(XPCFlag::Custom(0x201)), None, None))
|
||||
.await?;
|
||||
|
||||
debug!("Opening reply stream");
|
||||
self.h2_client.open_stream(REPLY_CHANNEL).await?;
|
||||
self.send_reply(XPCMessage::new(
|
||||
Some(XPCFlag::InitHandshake | XPCFlag::AlwaysSet),
|
||||
None,
|
||||
None,
|
||||
))
|
||||
.await?;
|
||||
|
||||
let mut total_msg = Vec::new();
|
||||
loop {
|
||||
// We receive from the root channel for this message
|
||||
total_msg.extend(self.h2_client.read(ROOT_CHANNEL).await?);
|
||||
let msg = match XPCMessage::decode(&total_msg) {
|
||||
Ok(m) => m,
|
||||
Err(IdeviceError::PacketSizeMismatch) => {
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
match msg.message {
|
||||
Some(msg) => {
|
||||
return Ok(msg.to_plist());
|
||||
}
|
||||
None => {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn recv(&mut self) -> Result<plist::Value, IdeviceError> {
|
||||
loop {
|
||||
let msg = self.h2_client.read(REPLY_CHANNEL).await?;
|
||||
|
||||
let msg = XPCMessage::decode(&msg)?;
|
||||
if let Some(msg) = msg.message {
|
||||
return Ok(msg.to_plist());
|
||||
}
|
||||
self.reply_id += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn recv_root(&mut self) -> Result<Option<plist::Value>, IdeviceError> {
|
||||
let msg = self.h2_client.read(ROOT_CHANNEL).await?;
|
||||
let msg = XPCMessage::decode(&msg)?;
|
||||
|
||||
if let Some(msg) = msg.message {
|
||||
Ok(Some(msg.to_plist()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_root(&mut self, msg: XPCMessage) -> Result<(), IdeviceError> {
|
||||
self.h2_client
|
||||
.send(msg.encode(self.root_id)?, ROOT_CHANNEL)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send_reply(&mut self, msg: XPCMessage) -> Result<(), IdeviceError> {
|
||||
self.h2_client
|
||||
.send(msg.encode(self.root_id)?, REPLY_CHANNEL)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ async fn main() {
|
||||
println!("rsd port: {}", tun_proxy.handshake.server_rsd_port);
|
||||
println!("-----------------------------");
|
||||
|
||||
let mut buf = vec![0; 1500];
|
||||
let mut buf = vec![0; 20_000]; // XPC is big lol
|
||||
loop {
|
||||
tokio::select! {
|
||||
Ok(len) = async_dev.recv(&mut buf) => {
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use std::{
|
||||
io::Write,
|
||||
net::{IpAddr, SocketAddr},
|
||||
str::FromStr,
|
||||
};
|
||||
use std::io::Write;
|
||||
|
||||
use clap::{Arg, Command};
|
||||
use idevice::{
|
||||
core_device_proxy::CoreDeviceProxy, debug_proxy::DebugProxyClient,
|
||||
tunneld::get_tunneld_devices, xpc::XPCDevice, IdeviceService, ReadWrite,
|
||||
core_device_proxy::CoreDeviceProxy, debug_proxy::DebugProxyClient, rsd::RsdHandshake,
|
||||
tcp::stream::AdapterStream, IdeviceService, RsdService,
|
||||
};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
mod common;
|
||||
|
||||
@@ -63,79 +58,31 @@ async fn main() {
|
||||
let pairing_file = matches.get_one::<String>("pairing_file");
|
||||
let host = matches.get_one::<String>("host");
|
||||
|
||||
let mut dp: DebugProxyClient<Box<dyn ReadWrite>> = if matches.get_flag("tunneld") {
|
||||
let socket = SocketAddr::new(
|
||||
IpAddr::from_str("127.0.0.1").unwrap(),
|
||||
idevice::tunneld::DEFAULT_PORT,
|
||||
);
|
||||
let mut devices = get_tunneld_devices(socket)
|
||||
.await
|
||||
.expect("Failed to get tunneld devices");
|
||||
|
||||
let (_udid, device) = match udid {
|
||||
Some(u) => (
|
||||
u.to_owned(),
|
||||
devices.remove(u).expect("Device not in tunneld"),
|
||||
),
|
||||
None => devices.into_iter().next().expect("No devices"),
|
||||
let provider =
|
||||
match common::get_provider(udid, host, pairing_file, "debug-proxy-jkcoxson").await {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Make the connection to RemoteXPC
|
||||
let client = XPCDevice::new(Box::new(
|
||||
TcpStream::connect((device.tunnel_address.as_str(), device.tunnel_port))
|
||||
.await
|
||||
.unwrap(),
|
||||
))
|
||||
let proxy = CoreDeviceProxy::connect(&*provider)
|
||||
.await
|
||||
.unwrap();
|
||||
.expect("no core proxy");
|
||||
let rsd_port = proxy.handshake.server_rsd_port;
|
||||
|
||||
// Get the debug proxy
|
||||
let service = client
|
||||
.services
|
||||
.get(idevice::debug_proxy::SERVICE_NAME)
|
||||
.expect("Client did not contain debug proxy service");
|
||||
|
||||
let stream = TcpStream::connect(SocketAddr::new(
|
||||
IpAddr::from_str(&device.tunnel_address).unwrap(),
|
||||
service.port,
|
||||
))
|
||||
let mut adapter = proxy.create_software_tunnel().expect("no software tunnel");
|
||||
let stream = AdapterStream::connect(&mut adapter, rsd_port)
|
||||
.await
|
||||
.expect("Failed to connect");
|
||||
.expect("no RSD connect");
|
||||
|
||||
DebugProxyClient::new(Box::new(stream))
|
||||
} else {
|
||||
let provider =
|
||||
match common::get_provider(udid, host, pairing_file, "debug-proxy-jkcoxson").await {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let proxy = CoreDeviceProxy::connect(&*provider)
|
||||
.await
|
||||
.expect("no core proxy");
|
||||
let rsd_port = proxy.handshake.server_rsd_port;
|
||||
// Make the connection to RemoteXPC
|
||||
let mut handshake = RsdHandshake::new(stream).await.unwrap();
|
||||
println!("{:?}", handshake.services);
|
||||
|
||||
let mut adapter = proxy.create_software_tunnel().expect("no software tunnel");
|
||||
adapter.connect(rsd_port).await.expect("no RSD connect");
|
||||
|
||||
// Make the connection to RemoteXPC
|
||||
let client = XPCDevice::new(Box::new(adapter)).await.unwrap();
|
||||
|
||||
// Get the debug proxy
|
||||
let service = client
|
||||
.services
|
||||
.get(idevice::debug_proxy::SERVICE_NAME)
|
||||
.expect("Client did not contain debug proxy service")
|
||||
.to_owned();
|
||||
|
||||
let mut adapter = client.into_inner();
|
||||
adapter.close().await.unwrap();
|
||||
adapter.connect(service.port).await.unwrap();
|
||||
|
||||
DebugProxyClient::new(Box::new(adapter))
|
||||
};
|
||||
let mut dp = DebugProxyClient::connect_rsd(&mut adapter, &mut handshake)
|
||||
.await
|
||||
.expect("no connect");
|
||||
|
||||
println!("Shell connected!");
|
||||
loop {
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
// Just lists apps for now
|
||||
|
||||
use clap::{Arg, Command};
|
||||
use idevice::{core_device_proxy::CoreDeviceProxy, xpc::XPCDevice, IdeviceService};
|
||||
use idevice::{
|
||||
core_device_proxy::CoreDeviceProxy, rsd::RsdHandshake, tcp::stream::AdapterStream,
|
||||
IdeviceService, RsdService,
|
||||
};
|
||||
|
||||
mod common;
|
||||
|
||||
@@ -69,26 +72,21 @@ async fn main() {
|
||||
let rsd_port = proxy.handshake.server_rsd_port;
|
||||
|
||||
let mut adapter = proxy.create_software_tunnel().expect("no software tunnel");
|
||||
adapter.connect(rsd_port).await.expect("no RSD connect");
|
||||
let stream = AdapterStream::connect(&mut adapter, rsd_port)
|
||||
.await
|
||||
.expect("no RSD connect");
|
||||
|
||||
// Make the connection to RemoteXPC
|
||||
let client = XPCDevice::new(Box::new(adapter)).await.unwrap();
|
||||
|
||||
// Get the debug proxy
|
||||
let service = client
|
||||
.services
|
||||
.get(idevice::dvt::SERVICE_NAME)
|
||||
.expect("Client did not contain DVT service")
|
||||
.to_owned();
|
||||
|
||||
let mut adapter = client.into_inner();
|
||||
adapter.connect(service.port).await.unwrap();
|
||||
|
||||
let mut rs_client = idevice::dvt::remote_server::RemoteServerClient::new(Box::new(adapter));
|
||||
rs_client.read_message(0).await.expect("no read??");
|
||||
let mut handshake = RsdHandshake::new(stream).await.unwrap();
|
||||
|
||||
let mut ls_client =
|
||||
idevice::dvt::location_simulation::LocationSimulationClient::new(&mut rs_client)
|
||||
idevice::dvt::remote_server::RemoteServerClient::connect_rsd(&mut adapter, &mut handshake)
|
||||
.await
|
||||
.expect("Failed to connect");
|
||||
ls_client.read_message(0).await.expect("no read??");
|
||||
|
||||
let mut ls_client =
|
||||
idevice::dvt::location_simulation::LocationSimulationClient::new(&mut ls_client)
|
||||
.await
|
||||
.expect("Unable to get channel for location simulation");
|
||||
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use std::{
|
||||
net::{IpAddr, SocketAddr},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use clap::{Arg, Command};
|
||||
use idevice::{
|
||||
core_device_proxy::CoreDeviceProxy, tunneld::get_tunneld_devices, xpc::XPCDevice,
|
||||
IdeviceService,
|
||||
core_device_proxy::CoreDeviceProxy, rsd::RsdHandshake, tcp::stream::AdapterStream,
|
||||
IdeviceService, RsdService,
|
||||
};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
mod common;
|
||||
|
||||
@@ -71,70 +65,8 @@ async fn main() {
|
||||
.get_one::<String>("bundle_id")
|
||||
.expect("No bundle ID specified");
|
||||
|
||||
if matches.get_flag("tunneld") {
|
||||
let socket = SocketAddr::new(
|
||||
IpAddr::from_str("127.0.0.1").unwrap(),
|
||||
idevice::tunneld::DEFAULT_PORT,
|
||||
);
|
||||
let mut devices = get_tunneld_devices(socket)
|
||||
.await
|
||||
.expect("Failed to get tunneld devices");
|
||||
|
||||
let (_udid, device) = match udid {
|
||||
Some(u) => (
|
||||
u.to_owned(),
|
||||
devices.remove(u).expect("Device not in tunneld"),
|
||||
),
|
||||
None => devices.into_iter().next().expect("No devices"),
|
||||
};
|
||||
|
||||
// Make the connection to RemoteXPC
|
||||
let client = XPCDevice::new(Box::new(
|
||||
TcpStream::connect((device.tunnel_address.as_str(), device.tunnel_port))
|
||||
.await
|
||||
.unwrap(),
|
||||
))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Get the debug proxy
|
||||
let service = client
|
||||
.services
|
||||
.get(idevice::dvt::SERVICE_NAME)
|
||||
.expect("Client did not contain DVT service");
|
||||
|
||||
let stream = TcpStream::connect(SocketAddr::new(
|
||||
IpAddr::from_str(&device.tunnel_address).unwrap(),
|
||||
service.port,
|
||||
))
|
||||
.await
|
||||
.expect("Failed to connect");
|
||||
|
||||
let mut rs_client = idevice::dvt::remote_server::RemoteServerClient::new(Box::new(stream));
|
||||
rs_client.read_message(0).await.expect("no read??");
|
||||
let mut pc_client =
|
||||
idevice::dvt::process_control::ProcessControlClient::new(&mut rs_client)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let pid = pc_client
|
||||
.launch_app(bundle_id, None, None, true, false)
|
||||
.await
|
||||
.expect("no launch??");
|
||||
pc_client
|
||||
.disable_memory_limit(pid)
|
||||
.await
|
||||
.expect("no disable??");
|
||||
println!("PID: {pid}");
|
||||
} else {
|
||||
let provider = match common::get_provider(
|
||||
udid,
|
||||
host,
|
||||
pairing_file,
|
||||
"process_control-jkcoxson",
|
||||
)
|
||||
.await
|
||||
{
|
||||
let provider =
|
||||
match common::get_provider(udid, host, pairing_file, "process_control-jkcoxson").await {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
@@ -142,45 +74,35 @@ async fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let proxy = CoreDeviceProxy::connect(&*provider)
|
||||
let proxy = CoreDeviceProxy::connect(&*provider)
|
||||
.await
|
||||
.expect("no core proxy");
|
||||
let rsd_port = proxy.handshake.server_rsd_port;
|
||||
|
||||
let mut adapter = proxy.create_software_tunnel().expect("no software tunnel");
|
||||
let stream = AdapterStream::connect(&mut adapter, rsd_port)
|
||||
.await
|
||||
.expect("no RSD connect");
|
||||
|
||||
// Make the connection to RemoteXPC
|
||||
let mut handshake = RsdHandshake::new(stream).await.unwrap();
|
||||
|
||||
let mut rs_client =
|
||||
idevice::dvt::remote_server::RemoteServerClient::connect_rsd(&mut adapter, &mut handshake)
|
||||
.await
|
||||
.expect("no core proxy");
|
||||
let rsd_port = proxy.handshake.server_rsd_port;
|
||||
.expect("no connect");
|
||||
rs_client.read_message(0).await.expect("no read??");
|
||||
let mut pc_client = idevice::dvt::process_control::ProcessControlClient::new(&mut rs_client)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut adapter = proxy.create_software_tunnel().expect("no software tunnel");
|
||||
adapter.connect(rsd_port).await.expect("no RSD connect");
|
||||
|
||||
// Make the connection to RemoteXPC
|
||||
let client = XPCDevice::new(Box::new(adapter)).await.unwrap();
|
||||
|
||||
// Get the debug proxy
|
||||
let service = client
|
||||
.services
|
||||
.get(idevice::dvt::SERVICE_NAME)
|
||||
.expect("Client did not contain DVT service")
|
||||
.to_owned();
|
||||
|
||||
let mut adapter = client.into_inner();
|
||||
adapter.connect(service.port).await.unwrap();
|
||||
|
||||
let mut rs_client = idevice::dvt::remote_server::RemoteServerClient::new(Box::new(adapter));
|
||||
rs_client.read_message(0).await.expect("no read??");
|
||||
let mut pc_client =
|
||||
idevice::dvt::process_control::ProcessControlClient::new(&mut rs_client)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let pid = pc_client
|
||||
.launch_app(bundle_id, None, None, true, false)
|
||||
.await
|
||||
.expect("no launch??");
|
||||
pc_client
|
||||
.disable_memory_limit(pid)
|
||||
.await
|
||||
.expect("no disable??");
|
||||
println!("PID: {pid}");
|
||||
|
||||
// let mut adapter = rs_client.into_inner();
|
||||
// adapter.close().await.expect("no close??");
|
||||
}
|
||||
let pid = pc_client
|
||||
.launch_app(bundle_id, None, None, true, false)
|
||||
.await
|
||||
.expect("no launch??");
|
||||
pc_client
|
||||
.disable_memory_limit(pid)
|
||||
.await
|
||||
.expect("no disable??");
|
||||
println!("PID: {pid}");
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
// Print out all the RemoteXPC services
|
||||
|
||||
use clap::{Arg, Command};
|
||||
use idevice::{core_device_proxy::CoreDeviceProxy, xpc::XPCDevice, IdeviceService};
|
||||
use idevice::{
|
||||
core_device_proxy::CoreDeviceProxy, tcp::stream::AdapterStream, xpc::RemoteXpcClient,
|
||||
IdeviceService,
|
||||
};
|
||||
|
||||
mod common;
|
||||
|
||||
@@ -63,10 +66,13 @@ async fn main() {
|
||||
let rsd_port = proxy.handshake.server_rsd_port;
|
||||
|
||||
let mut adapter = proxy.create_software_tunnel().expect("no software tunnel");
|
||||
adapter.connect(rsd_port).await.expect("no RSD connect");
|
||||
adapter.pcap("new_xpc.pcap").await.unwrap();
|
||||
let conn = AdapterStream::connect(&mut adapter, rsd_port)
|
||||
.await
|
||||
.expect("no RSD connect");
|
||||
|
||||
// Make the connection to RemoteXPC
|
||||
let client = XPCDevice::new(Box::new(adapter)).await.unwrap();
|
||||
let mut client = RemoteXpcClient::new(Box::new(conn)).await.unwrap();
|
||||
|
||||
println!("{:#?}", client.services);
|
||||
println!("{:#?}", client.do_handshake().await);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user