mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 14:36:16 +01:00
Add missing usbmuxd functions to FFI
This commit is contained in:
@@ -1,18 +1,22 @@
|
|||||||
// Jackson Coxson
|
// Jackson Coxson
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
ffi::{CStr, c_char},
|
ffi::{CStr, CString, c_char},
|
||||||
ptr::null_mut,
|
ptr::null_mut,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{IdeviceFfiError, RUNTIME, ffi_err, util::c_socket_to_rust};
|
use crate::{
|
||||||
|
IdeviceFfiError, IdeviceHandle, IdevicePairingFile, RUNTIME, ffi_err, util::c_socket_to_rust,
|
||||||
|
};
|
||||||
use idevice::{
|
use idevice::{
|
||||||
IdeviceError,
|
IdeviceError,
|
||||||
usbmuxd::{UsbmuxdAddr, UsbmuxdConnection},
|
usbmuxd::{UsbmuxdAddr, UsbmuxdConnection, UsbmuxdDevice},
|
||||||
};
|
};
|
||||||
|
use log::error;
|
||||||
|
|
||||||
pub struct UsbmuxdConnectionHandle(pub UsbmuxdConnection);
|
pub struct UsbmuxdConnectionHandle(pub UsbmuxdConnection);
|
||||||
pub struct UsbmuxdAddrHandle(pub UsbmuxdAddr);
|
pub struct UsbmuxdAddrHandle(pub UsbmuxdAddr);
|
||||||
|
pub struct UsbmuxdDeviceHandle(pub UsbmuxdDevice);
|
||||||
|
|
||||||
/// Connects to a usbmuxd instance over TCP
|
/// Connects to a usbmuxd instance over TCP
|
||||||
///
|
///
|
||||||
@@ -108,6 +112,7 @@ pub unsafe extern "C" fn idevice_usbmuxd_new_unix_socket_connection(
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
/// `addr` must be a valid CStr
|
/// `addr` must be a valid CStr
|
||||||
/// `usbmuxd_connection` must be a valid, non-null pointer to a location where the handle will be stored
|
/// `usbmuxd_connection` must be a valid, non-null pointer to a location where the handle will be stored
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn idevice_usbmuxd_new_default_connection(
|
pub unsafe extern "C" fn idevice_usbmuxd_new_default_connection(
|
||||||
tag: u32,
|
tag: u32,
|
||||||
usbmuxd_connection: *mut *mut UsbmuxdConnectionHandle,
|
usbmuxd_connection: *mut *mut UsbmuxdConnectionHandle,
|
||||||
@@ -133,6 +138,203 @@ pub unsafe extern "C" fn idevice_usbmuxd_new_default_connection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a list of connected devices from usbmuxd.
|
||||||
|
///
|
||||||
|
/// The returned list must be freed with `idevice_usbmuxd_device_list_free`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `usbmuxd_conn` - A valid connection to usbmuxd.
|
||||||
|
/// * `devices` - A pointer to a C-style array of `UsbmuxdDeviceHandle` pointers. On success, this will be filled.
|
||||||
|
/// * `count` - A pointer to an integer. On success, this will be filled with the number of devices found.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An `IdeviceFfiError` on error, `null` on success.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// * `usbmuxd_conn` must be a valid pointer.
|
||||||
|
/// * `devices` and `count` must be valid, non-null pointers.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_usbmuxd_get_devices(
|
||||||
|
usbmuxd_conn: *mut UsbmuxdConnectionHandle,
|
||||||
|
devices: *mut *mut *mut UsbmuxdDeviceHandle,
|
||||||
|
count: *mut libc::c_int,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if usbmuxd_conn.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
let conn = unsafe { &mut (*usbmuxd_conn).0 };
|
||||||
|
|
||||||
|
let res = RUNTIME.block_on(async { conn.get_devices().await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(device_vec) => {
|
||||||
|
unsafe {
|
||||||
|
*count = device_vec.len() as libc::c_int;
|
||||||
|
}
|
||||||
|
let mut c_arr = Vec::with_capacity(device_vec.len());
|
||||||
|
for device in device_vec {
|
||||||
|
let handle = Box::new(UsbmuxdDeviceHandle(device));
|
||||||
|
c_arr.push(Box::into_raw(handle));
|
||||||
|
}
|
||||||
|
let mut c_arr = c_arr.into_boxed_slice();
|
||||||
|
unsafe {
|
||||||
|
*devices = c_arr.as_mut_ptr();
|
||||||
|
}
|
||||||
|
std::mem::forget(c_arr); // Prevent deallocation of the slice's buffer
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connects to a service on a given device.
|
||||||
|
///
|
||||||
|
/// This function consumes the `UsbmuxdConnectionHandle`. The handle will be invalid after this call
|
||||||
|
/// and must not be used again. The caller is NOT responsible for freeing it.
|
||||||
|
/// A new `IdeviceHandle` is returned on success, which must be freed by the caller.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `usbmuxd_connection` - The connection to use. It will be consumed.
|
||||||
|
/// * `device_id` - The ID of the device to connect to.
|
||||||
|
/// * `port` - The TCP port on the device to connect to.
|
||||||
|
/// * `idevice` - On success, points to the new device connection handle.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An `IdeviceFfiError` on error, `null` on success.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// * `usbmuxd_connection` must be a valid pointer allocated by this library and never used again.
|
||||||
|
/// The value is consumed.
|
||||||
|
/// * `idevice` must be a valid, non-null pointer.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_usbmuxd_connect_to_device(
|
||||||
|
usbmuxd_connection: *mut UsbmuxdConnectionHandle,
|
||||||
|
device_id: u32,
|
||||||
|
port: u16,
|
||||||
|
label: *const c_char,
|
||||||
|
idevice: *mut *mut IdeviceHandle,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if usbmuxd_connection.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take ownership of the connection handle
|
||||||
|
let conn = unsafe {
|
||||||
|
let conn = std::ptr::read(&(*usbmuxd_connection).0); // move the inner connection
|
||||||
|
drop(Box::from_raw(usbmuxd_connection)); // free the wrapper
|
||||||
|
conn
|
||||||
|
};
|
||||||
|
|
||||||
|
let label = unsafe {
|
||||||
|
match CStr::from_ptr(label).to_str() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = RUNTIME.block_on(async move { conn.connect_to_device(device_id, port, label).await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(device_conn) => {
|
||||||
|
let boxed = Box::new(IdeviceHandle(device_conn));
|
||||||
|
unsafe {
|
||||||
|
*idevice = Box::into_raw(boxed);
|
||||||
|
}
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the pairing record for a given device UDID.
|
||||||
|
///
|
||||||
|
/// The returned `PairingFileHandle` must be freed with `idevice_pair_record_free`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `usbmuxd_conn` - A valid connection to usbmuxd.
|
||||||
|
/// * `udid` - The UDID of the device.
|
||||||
|
/// * `pair_record` - On success, points to the new pairing file handle.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An `IdeviceFfiError` on error, `null` on success.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// * `usbmuxd_conn` must be a valid pointer.
|
||||||
|
/// * `udid` must be a valid, null-terminated C string.
|
||||||
|
/// * `pair_record` must be a valid, non-null pointer.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_usbmuxd_get_pair_record(
|
||||||
|
usbmuxd_conn: *mut UsbmuxdConnectionHandle,
|
||||||
|
udid: *const c_char,
|
||||||
|
pair_record: *mut *mut IdevicePairingFile,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if usbmuxd_conn.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
let conn = unsafe { &mut (*usbmuxd_conn).0 };
|
||||||
|
|
||||||
|
let udid_str = unsafe {
|
||||||
|
match CStr::from_ptr(udid).to_str() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = RUNTIME.block_on(async { conn.get_pair_record(udid_str).await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(pf) => {
|
||||||
|
let boxed = Box::new(IdevicePairingFile(pf));
|
||||||
|
unsafe {
|
||||||
|
*pair_record = Box::into_raw(boxed);
|
||||||
|
}
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the BUID (Boot-Unique ID) from usbmuxd.
|
||||||
|
///
|
||||||
|
/// The returned string must be freed with `idevice_string_free`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `usbmuxd_conn` - A valid connection to usbmuxd.
|
||||||
|
/// * `buid` - On success, points to a newly allocated, null-terminated C string.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// An `IdeviceFfiError` on error, `null` on success.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// * `usbmuxd_conn` must be a valid pointer.
|
||||||
|
/// * `buid` must be a valid, non-null pointer.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_usbmuxd_get_buid(
|
||||||
|
usbmuxd_conn: *mut UsbmuxdConnectionHandle,
|
||||||
|
buid: *mut *mut c_char,
|
||||||
|
) -> *mut IdeviceFfiError {
|
||||||
|
if usbmuxd_conn.is_null() {
|
||||||
|
return ffi_err!(IdeviceError::FfiInvalidArg);
|
||||||
|
}
|
||||||
|
let conn = unsafe { &mut (*usbmuxd_conn).0 };
|
||||||
|
|
||||||
|
let res = RUNTIME.block_on(async { conn.get_buid().await });
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(buid_str) => match CString::new(buid_str) {
|
||||||
|
Ok(c_str) => {
|
||||||
|
unsafe { *buid = c_str.into_raw() };
|
||||||
|
null_mut()
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Unable to convert BUID string to CString: {e:?}. Null interior byte.");
|
||||||
|
ffi_err!(IdeviceError::UnexpectedResponse)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => ffi_err!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Frees a UsbmuxdConnection handle
|
/// Frees a UsbmuxdConnection handle
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -225,3 +427,108 @@ pub unsafe extern "C" fn idevice_usbmuxd_addr_free(usbmuxd_addr: *mut UsbmuxdAdd
|
|||||||
let _ = unsafe { Box::from_raw(usbmuxd_addr) };
|
let _ = unsafe { Box::from_raw(usbmuxd_addr) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Frees a list of devices returned by `idevice_usbmuxd_get_devices`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `devices` - The array of device handles to free.
|
||||||
|
/// * `count` - The number of elements in the array.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `devices` must be a valid pointer to an array of `count` device handles
|
||||||
|
/// allocated by this library, or NULL.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_usbmuxd_device_list_free(
|
||||||
|
devices: *mut *mut UsbmuxdDeviceHandle,
|
||||||
|
count: libc::c_int,
|
||||||
|
) {
|
||||||
|
if devices.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let slice = unsafe { std::slice::from_raw_parts_mut(devices, count as usize) };
|
||||||
|
for &mut ptr in slice {
|
||||||
|
if !ptr.is_null() {
|
||||||
|
let _ = unsafe { Box::from_raw(ptr) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frees a usbmuxd device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `device` - The device handle to free.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `device` must be a valid pointer to the device handle
|
||||||
|
/// allocated by this library, or NULL.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_usbmuxd_device_free(device: *mut UsbmuxdDeviceHandle) {
|
||||||
|
if device.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let _ = unsafe { Box::from_raw(device) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the UDID from a device handle.
|
||||||
|
/// The returned string must be freed by the caller using `idevice_string_free`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `device` must be a valid pointer to a `UsbmuxdDeviceHandle`.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_usbmuxd_device_get_udid(
|
||||||
|
device: *const UsbmuxdDeviceHandle,
|
||||||
|
) -> *mut c_char {
|
||||||
|
if device.is_null() {
|
||||||
|
return null_mut();
|
||||||
|
}
|
||||||
|
let device = unsafe { &(*device).0 };
|
||||||
|
match CString::new(device.udid.as_str()) {
|
||||||
|
Ok(s) => s.into_raw(),
|
||||||
|
Err(_) => null_mut(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the device ID from a device handle.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `device` must be a valid pointer to a `UsbmuxdDeviceHandle`.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_usbmuxd_device_get_device_id(
|
||||||
|
device: *const UsbmuxdDeviceHandle,
|
||||||
|
) -> u32 {
|
||||||
|
if device.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
unsafe { (*device).0.device_id }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
enum UsbmuxdConnectionType {
|
||||||
|
Usb = 1,
|
||||||
|
Network = 2,
|
||||||
|
Unknown = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the connection type (UsbmuxdConnectionType) from a device handle.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// The enum value of the connection type, or 0 for null device handles
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `device` must be a valid pointer to a `UsbmuxdDeviceHandle`.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn idevice_usbmuxd_device_get_connection_type(
|
||||||
|
device: *const UsbmuxdDeviceHandle,
|
||||||
|
) -> u8 {
|
||||||
|
if device.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let ct = unsafe { &(*device).0.connection_type };
|
||||||
|
|
||||||
|
let ct = match ct {
|
||||||
|
idevice::usbmuxd::Connection::Usb => UsbmuxdConnectionType::Usb,
|
||||||
|
idevice::usbmuxd::Connection::Network(_) => UsbmuxdConnectionType::Network,
|
||||||
|
idevice::usbmuxd::Connection::Unknown(_) => UsbmuxdConnectionType::Unknown,
|
||||||
|
};
|
||||||
|
ct as u8
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user