diff --git a/ffi/src/usbmuxd.rs b/ffi/src/usbmuxd.rs index 3f0b932..05eb650 100644 --- a/ffi/src/usbmuxd.rs +++ b/ffi/src/usbmuxd.rs @@ -2,6 +2,7 @@ use std::{ ffi::{CStr, CString, c_char}, + pin::Pin, ptr::null_mut, }; @@ -9,15 +10,19 @@ use crate::{ IdeviceFfiError, IdeviceHandle, IdevicePairingFile, RUNTIME, ffi_err, util::{SockAddr, c_socket_to_rust, idevice_sockaddr, idevice_socklen_t}, }; +use futures::{Stream, StreamExt}; use idevice::{ IdeviceError, - usbmuxd::{UsbmuxdAddr, UsbmuxdConnection, UsbmuxdDevice}, + usbmuxd::{UsbmuxdAddr, UsbmuxdConnection, UsbmuxdDevice, UsbmuxdListenEvent}, }; use log::error; pub struct UsbmuxdConnectionHandle(pub UsbmuxdConnection); pub struct UsbmuxdAddrHandle(pub UsbmuxdAddr); pub struct UsbmuxdDeviceHandle(pub UsbmuxdDevice); +pub struct UsbmuxdListenerHandle<'a>( + Pin> + 'a>>, +); /// Connects to a usbmuxd instance over TCP /// @@ -301,6 +306,153 @@ pub unsafe extern "C" fn idevice_usbmuxd_get_pair_record( } } +/// Saves the pairing record for a given device UDID. +/// +/// # Arguments +/// * `usbmuxd_conn` - A valid connection to usbmuxd. +/// * `device_id` - The muxer ID for the device +/// * `udid` - The UDID of the device. +/// * `pair_record` - The bytes of the pairing record plist to save +/// * `pair_record_len` - the length of the pairing record bytes +/// +/// # Returns +/// An `IdeviceFfiError` on error, `null` on success. +/// +/// # Safety +/// * `usbmuxd_conn` must be a valid pointer. +/// * `udid` must be a valid, null-terminated C string. +/// * `pair_record` must be a valid, non-null pointer. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn idevice_usbmuxd_save_pair_record( + usbmuxd_conn: *mut UsbmuxdConnectionHandle, + device_id: u32, + udid: *const c_char, + pair_record: *mut u8, + pair_record_len: usize, +) -> *mut IdeviceFfiError { + if usbmuxd_conn.is_null() || pair_record.is_null() { + return ffi_err!(IdeviceError::FfiInvalidArg); + } + let conn = unsafe { &mut (*usbmuxd_conn).0 }; + let pair_record = + unsafe { std::slice::from_raw_parts_mut(pair_record, pair_record_len) }.to_vec(); + + let udid_str = unsafe { + match CStr::from_ptr(udid).to_str() { + Ok(s) => s, + Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg), + } + }; + + let res = RUNTIME.block_on(async { + conn.save_pair_record(device_id, udid_str, pair_record) + .await + }); + + match res { + Ok(_) => null_mut(), + Err(e) => ffi_err!(e), + } +} + +/// Listens on the socket for connections and disconnections +/// +/// # Safety +/// Pass valid pointers. Free the stream with ``idevice_usbmuxd_listener_handle_free``. +/// The stream must outlive the usbmuxd connection, and the usbmuxd connection cannot +/// be used for other requests. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn idevice_usbmuxd_listen( + usbmuxd_conn: *mut UsbmuxdConnectionHandle, + stream_handle: *mut *mut UsbmuxdListenerHandle, +) -> *mut IdeviceFfiError { + if usbmuxd_conn.is_null() || stream_handle.is_null() { + return ffi_err!(IdeviceError::FfiInvalidArg); + } + let conn = unsafe { &mut (*usbmuxd_conn).0 }; + + let res = RUNTIME.block_on(async { conn.listen().await }); + + match res { + Ok(s) => { + unsafe { *stream_handle = Box::into_raw(Box::new(UsbmuxdListenerHandle(s))) }; + null_mut() + } + Err(e) => ffi_err!(e), + } +} + +/// Frees a stream created by ``listen`` or does nothing on null +/// +/// # Safety +/// Pass a valid pointer. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn idevice_usbmuxd_listener_handle_free( + stream_handle: *mut UsbmuxdListenerHandle, +) { + if stream_handle.is_null() { + return; + } + let _ = unsafe { Box::from_raw(stream_handle) }; +} + +/// Gets the next event from the stream. +/// Connect will be set to true if the event is a connection event, +/// and the connection_device will be filled with the device information. +/// If connection is false, the mux ID of the device will be filled. +/// +/// # Arguments +/// * `stream_handle` - The handle to the stream returned by listen +/// * `connect` - The bool that will be set +/// * `connection_device` - The pointer that will be filled on a connect event +/// * `disconnection_id` - The mux ID that will be set on a disconnect event +/// +/// # Safety +/// Pass valid pointers +#[unsafe(no_mangle)] +pub unsafe extern "C" fn idevice_usbmuxd_listener_next( + stream_handle: *mut UsbmuxdListenerHandle, + connect: *mut bool, + connection_device: *mut *mut UsbmuxdDeviceHandle, + disconnection_id: *mut u32, +) -> *mut IdeviceFfiError { + if stream_handle.is_null() + || connect.is_null() + || connection_device.is_null() + || disconnection_id.is_null() + { + return ffi_err!(IdeviceError::FfiInvalidArg); + } + let stream = unsafe { &mut (*stream_handle).0 }; + + let res = RUNTIME.block_on(async { stream.next().await }); + + match res { + Some(res) => match res { + Ok(s) => { + match s { + UsbmuxdListenEvent::Connected(usbmuxd_device) => { + unsafe { *connect = true }; + unsafe { + *connection_device = + Box::into_raw(Box::new(UsbmuxdDeviceHandle(usbmuxd_device))) + }; + } + UsbmuxdListenEvent::Disconnected(id) => unsafe { *disconnection_id = id }, + } + null_mut() + } + Err(e) => ffi_err!(e), + }, + None => { + ffi_err!(IdeviceError::Socket(std::io::Error::new( + std::io::ErrorKind::BrokenPipe, + "end of stream" + ))) + } + } +} + /// Reads the BUID (Boot-Unique ID) from usbmuxd. /// /// The returned string must be freed with `idevice_string_free`.