diff --git a/INTEGRATION.md b/INTEGRATION.md index 4319381..e725ebd 100644 --- a/INTEGRATION.md +++ b/INTEGRATION.md @@ -209,7 +209,7 @@ let paths = DDIPaths.default( ) ``` -Use the same `DDIPaths` for preparation, JIT enablement, and cache reset. Do not validate cached DDI versions or contents yourself; StikJIT reuses readable, nonempty files and lets the image mounter decide whether they work. +Use the same `DDIPaths` for preparation, JIT enablement, and cache reset. The cache contains the image, trust cache, build manifest, cryptex info, and root hash. Do not validate cached DDI versions or contents yourself; StikJIT reuses readable, nonempty files and lets the device's cryptex service decide whether they work. A successful DDI mount persists until the device reboots, so it normally needs to be mounted only once per boot. diff --git a/Sources/DDISession.swift b/Sources/DDISession.swift index f418783..fd88cf3 100644 --- a/Sources/DDISession.swift +++ b/Sources/DDISession.swift @@ -1,18 +1,6 @@ import Foundation @_implementationOnly import idevice -private func ddiMountProgressCallback(progress: size_t, total: size_t, context: UnsafeMutableRawPointer?) { - guard let context else { return } - let progressHandler = Unmanaged.fromOpaque(context).takeUnretainedValue() - let fraction = total > 0 ? Double(progress) / Double(total) : 0 - progressHandler.handler(fraction) -} - -private final class DDIProgressBox { - let handler: (Double) -> Void - init(_ handler: @escaping (Double) -> Void) { self.handler = handler } -} - final class DDISession { private struct Tunnel { @@ -36,6 +24,25 @@ final class DDISession { let tunnel = try makeTunnel() defer { tunnel.free() } + var installed: UnsafeMutablePointer? + let cryptexError = cryptexd_installed_ddi(tunnel.adapter, tunnel.handshake, &installed) + defer { cryptexd_free_installed_cryptex(installed) } + if installed != nil { + if let cryptexError { idevice_error_free(cryptexError) } + return true + } + + if (try? isLegacyDDIMounted(over: tunnel)) == true { + if let cryptexError { idevice_error_free(cryptexError) } + return true + } + if let cryptexError { + try IdeviceFFI.check("failed to query installed DDI cryptex") { cryptexError } + } + return false + } + + private func isLegacyDDIMounted(over tunnel: Tunnel) throws -> Bool { var client: OpaquePointer? try IdeviceFFI.check("failed to connect to image mounter") { image_mounter_connect_rsd(tunnel.adapter, tunnel.handshake, &client) @@ -57,81 +64,24 @@ final class DDISession { return deviceCount > 0 } - func mountDDI(imagePath: String, trustcachePath: String, manifestPath: String, progress: @escaping (Double) -> Void) throws { - let imageData = try mappedFileData(atPath: imagePath, description: "developer disk image") - let trustcacheData = try mappedFileData(atPath: trustcachePath, description: "developer disk image trust cache") - let manifestData = try mappedFileData(atPath: manifestPath, description: "developer disk image manifest") + func mountDDI(from directoryPath: String, progress: @escaping (Double) -> Void) throws { + progress(0) + var assets: OpaquePointer? + try IdeviceFFI.check("failed to load DDI cryptex assets") { + directoryPath.withCString { cryptex1_assets_load($0, &assets) } + } + guard let assets else { + throw StikJITError.ddiFilesMissing("DDI cryptex assets could not be loaded from \(directoryPath)") + } + defer { cryptex1_assets_free(assets) } let tunnel = try makeTunnel() defer { tunnel.free() } - let uniqueChipID = try fetchUniqueChipID(over: tunnel) - - var client: OpaquePointer? - try IdeviceFFI.check("failed to connect to image mounter") { - image_mounter_connect_rsd(tunnel.adapter, tunnel.handshake, &client) + try IdeviceFFI.check("failed to install DDI cryptex") { + cryptexd_install_ddi(tunnel.adapter, tunnel.handshake, assets, nil) } - guard let client else { throw StikJITError.debugProxyUnavailable } - defer { image_mounter_free(client) } - - let progressBox = DDIProgressBox(progress) - let context = Unmanaged.passUnretained(progressBox).toOpaque() - - try IdeviceFFI.check("failed to mount personalized developer disk image") { - imageData.withUnsafeBytes { imageBuffer -> UnsafeMutablePointer? in - trustcacheData.withUnsafeBytes { trustcacheBuffer -> UnsafeMutablePointer? in - manifestData.withUnsafeBytes { manifestBuffer -> UnsafeMutablePointer? in - image_mounter_mount_personalized_with_callback_rsd( - client, - tunnel.adapter, - tunnel.handshake, - imageBuffer.bindMemory(to: UInt8.self).baseAddress, - imageData.count, - trustcacheBuffer.bindMemory(to: UInt8.self).baseAddress, - trustcacheData.count, - manifestBuffer.bindMemory(to: UInt8.self).baseAddress, - manifestData.count, - nil, - uniqueChipID, - ddiMountProgressCallback, - context) - } - } - } - } - } - - private func mappedFileData(atPath path: String, description: String) throws -> Data { - guard FileManager.default.fileExists(atPath: path) else { - throw StikJITError.ddiFilesMissing("\(description) not found at \(path)") - } - let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) - guard !data.isEmpty else { - throw StikJITError.ddiFilesMissing("\(description) is empty at \(path)") - } - return data - } - - private func fetchUniqueChipID(over tunnel: Tunnel) throws -> UInt64 { - var client: OpaquePointer? - try IdeviceFFI.check("failed to connect to lockdownd") { - lockdownd_connect_rsd(tunnel.adapter, tunnel.handshake, &client) - } - guard let client else { throw StikJITError.debugProxyUnavailable } - defer { lockdownd_client_free(client) } - - var uniqueChipIDPlist: plist_t? - try IdeviceFFI.check("failed to query UniqueChipID") { - "UniqueChipID".withCString { lockdownd_get_value(client, $0, nil, &uniqueChipIDPlist) } - } - guard let uniqueChipIDPlist else { - throw StikJITError.device(code: -1, subCode: 0, message: "UniqueChipID was not returned by lockdownd") - } - defer { plist_free(uniqueChipIDPlist) } - - var value: UInt64 = 0 - plist_get_uint_val(uniqueChipIDPlist, &value) - return value + progress(1) } private func openPairingFile() throws -> OpaquePointer { diff --git a/Sources/DeveloperDiskImageService.swift b/Sources/DeveloperDiskImageService.swift index 745b13f..052b6c1 100644 --- a/Sources/DeveloperDiskImageService.swift +++ b/Sources/DeveloperDiskImageService.swift @@ -8,10 +8,20 @@ public struct DDIPaths: Sendable { public var manifestPath: String - public init(imagePath: String, trustcachePath: String, manifestPath: String) { + public var cryptexInfoPath: String + + public var rootHashPath: String + + public init(imagePath: String, + trustcachePath: String, + manifestPath: String, + cryptexInfoPath: String? = nil, + rootHashPath: String? = nil) { self.imagePath = imagePath self.trustcachePath = trustcachePath self.manifestPath = manifestPath + self.cryptexInfoPath = cryptexInfoPath ?? "\(imagePath).cryptex_info" + self.rootHashPath = rootHashPath ?? "\(imagePath).root_hash" } public static func `default`(in directory: URL) -> DDIPaths { @@ -35,7 +45,7 @@ public struct DDIPaths: Sendable { } var allPaths: [String] { - [imagePath, trustcachePath, manifestPath] + [imagePath, trustcachePath, manifestPath, cryptexInfoPath, rootHashPath] } func removeCachedFiles() throws { @@ -53,13 +63,15 @@ struct DDIDownloadItem { } enum DDIDownloadCatalog { - private static let baseURL = URL(string: "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized")! + private static let baseURL = URL(string: "https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Cryptex")! static func items(for paths: DDIPaths) -> [DDIDownloadItem] { [ DDIDownloadItem(name: "BuildManifest.plist", destinationPath: paths.manifestPath, url: baseURL.appendingPathComponent("BuildManifest.plist")), DDIDownloadItem(name: "Image.dmg", destinationPath: paths.imagePath, url: baseURL.appendingPathComponent("Image.dmg")), DDIDownloadItem(name: "Image.dmg.trustcache", destinationPath: paths.trustcachePath, url: baseURL.appendingPathComponent("Image.dmg.trustcache")), + DDIDownloadItem(name: "Image.dmg.cryptex_info", destinationPath: paths.cryptexInfoPath, url: baseURL.appendingPathComponent("Image.dmg.cryptex_info")), + DDIDownloadItem(name: "Image.dmg.root_hash", destinationPath: paths.rootHashPath, url: baseURL.appendingPathComponent("Image.dmg.root_hash")), ] } } diff --git a/Sources/StikJIT.swift b/Sources/StikJIT.swift index 6b7b02a..1260b02 100644 --- a/Sources/StikJIT.swift +++ b/Sources/StikJIT.swift @@ -144,9 +144,7 @@ public enum StikJIT { progress(.downloadingDDI(fraction: fraction, status: status)) } } - try session.mountDDI(imagePath: paths.imagePath, - trustcachePath: paths.trustcachePath, - manifestPath: paths.manifestPath) { fraction in + try session.mountDDI(from: URL(fileURLWithPath: paths.manifestPath).deletingLastPathComponent().path) { fraction in progress(.mountingDDI(fraction: fraction)) } progress(.verifyingDDI) @@ -183,9 +181,7 @@ public enum StikJIT { paths: DDIPaths, progress: @escaping (Double) -> Void = { _ in }) throws { let session = DDISession(pairingFilePath: pairingFile.path, configuration: configuration) - try session.mountDDI(imagePath: paths.imagePath, - trustcachePath: paths.trustcachePath, - manifestPath: paths.manifestPath, - progress: progress) + try session.mountDDI(from: URL(fileURLWithPath: paths.manifestPath).deletingLastPathComponent().path, + progress: progress) } } diff --git a/idevice/idevice.h b/idevice/idevice.h index b429ce2..2aef885 100644 --- a/idevice/idevice.h +++ b/idevice/idevice.h @@ -27,6 +27,26 @@ #define LOCKDOWN_PORT 62078 +/** + * The nonce domain index cryptexes are personalized against + */ +#define IDEVICE_CRYPTEXD_NONCE_DOMAIN_CRYPTEX 2 + +/** + * The `image-type-index` a DeveloperDiskImage install uses + */ +#define IDEVICE_CRYPTEXD_DDI_IMAGE_TYPE_INDEX 10 + +/** + * The `persistence` a DeveloperDiskImage install uses + */ +#define IDEVICE_CRYPTEXD_DDI_PERSISTENCE 2 + +/** + * The `nonce-persistence` a DeveloperDiskImage install uses + */ +#define IDEVICE_CRYPTEXD_DDI_NONCE_PERSISTENCE 1 + typedef enum AfcFopenMode { AfcRdOnly = 1, AfcRw = 2, @@ -44,6 +64,46 @@ typedef enum AfcLinkType { Symbolic = 2, } AfcLinkType; +/** + * The system's light/dark appearance + */ +typedef enum IdeviceUserInterfaceStyle { + IdeviceUserInterfaceStyleLight = 0, + IdeviceUserInterfaceStyleDark = 1, +} IdeviceUserInterfaceStyle; + +/** + * Which of the device's filesystem domains a session is scoped to + */ +typedef enum IdeviceFileServiceDomain { + /** + * An app's own data container. The identifier is the bundle ID. + */ + IdeviceFileServiceDomainAppDataContainer = 1, + /** + * A shared app-group container. The identifier is the group ID. + */ + IdeviceFileServiceDomainAppGroupDataContainer = 2, + /** + * The temporary directory. + */ + IdeviceFileServiceDomainTemporary = 3, + /** + * The system crash-log store. + */ + IdeviceFileServiceDomainSystemCrashLogs = 5, +} IdeviceFileServiceDomain; + +/** + * Network event type discriminant + */ +typedef enum IdeviceNetworkEventType { + InterfaceDetection = 0, + ConnectionDetection = 1, + ConnectionUpdate = 2, + Unknown = 255, +} IdeviceNetworkEventType; + typedef enum IdeviceLoggerError { Success = 0, FileError = -1, @@ -60,6 +120,20 @@ typedef enum IdeviceLogLevel { Trace = 5, } IdeviceLogLevel; +/** + * The outcome of a `CreateStashbag` request. + */ +typedef enum IdeviceStashbagOutcome { + /** + * The device does not need a stashbag; nothing further to do. + */ + NotRequired = 0, + /** + * A stashbag was created and must be committed with the AP ticket. + */ + CommitRequired = 1, +} IdeviceStashbagOutcome; + typedef struct AdapterHandle AdapterHandle; typedef struct AdapterStreamHandle AdapterStreamHandle; @@ -78,19 +152,53 @@ typedef struct AmfiClientHandle AmfiClientHandle; */ typedef struct AppServiceHandle AppServiceHandle; +/** + * Opaque handle to an ApplicationListingClient + */ +typedef struct ApplicationListingHandle ApplicationListingHandle; + typedef struct BtPacketLoggerClientHandle BtPacketLoggerClientHandle; typedef struct CompanionProxyClientHandle CompanionProxyClientHandle; +/** + * Opaque handle to a ConditionInducerClient + */ +typedef struct ConditionInducerHandle ConditionInducerHandle; + +/** + * Opaque handle to a ConfigurationServiceClient + */ +typedef struct ConfigurationServiceHandle ConfigurationServiceHandle; + typedef struct CoreDeviceProxyHandle CoreDeviceProxyHandle; typedef struct CrashReportCopyMobileHandle CrashReportCopyMobileHandle; +/** + * Opaque handle to the payloads a Cryptex1 DeveloperDiskImage install needs + */ +typedef struct Cryptex1AssetsHandle Cryptex1AssetsHandle; + +/** + * Opaque handle to a CryptexdClient + * + * The daemon serves one routine per connection, so every call below consumes + * the handle: it is freed by the call and must not be used again, even when + * the call fails. + */ +typedef struct CryptexdHandle CryptexdHandle; + /** * Opaque handle to a DebugProxyClient */ typedef struct DebugProxyHandle DebugProxyHandle; +/** + * Opaque handle to a DeviceInfoClient + */ +typedef struct DeviceInfoHandle DeviceInfoHandle; + typedef struct DiagnosticsRelayClientHandle DiagnosticsRelayClientHandle; /** @@ -98,10 +206,24 @@ typedef struct DiagnosticsRelayClientHandle DiagnosticsRelayClientHandle; */ typedef struct DiagnosticsServiceHandle DiagnosticsServiceHandle; +typedef struct EnergyMonitorHandle EnergyMonitorHandle; + +/** + * Opaque handle to a FileServiceClient + */ +typedef struct FileServiceHandle FileServiceHandle; + +typedef struct GraphicsHandle GraphicsHandle; + typedef struct HeartbeatClientHandle HeartbeatClientHandle; typedef struct HouseArrestClientHandle HouseArrestClientHandle; +/** + * Opaque handle to an IconServiceClient + */ +typedef struct IconServiceHandle IconServiceHandle; + /** * Opaque C-compatible handle to an Idevice connection */ @@ -114,6 +236,16 @@ typedef struct IdevicePairingFile IdevicePairingFile; typedef struct IdeviceProviderHandle IdeviceProviderHandle; +/** + * An opaque, shareable cancellation flag for an in-flight restore. + * + * Create one with `idevice_restore_cancel_handle_new`, pass it to + * `idevice_restore_run`, and call `idevice_restore_cancel` from another thread to + * request a graceful cancel (the device is rebooted toward recovery). Free it with + * `idevice_restore_cancel_handle_free` once the restore has returned. + */ +typedef struct IdeviceRestoreCancelHandle IdeviceRestoreCancelHandle; + typedef struct IdeviceSocketHandle IdeviceSocketHandle; typedef struct ImageMounterHandle ImageMounterHandle; @@ -122,6 +254,11 @@ typedef struct InstallationProxyClientHandle InstallationProxyClientHandle; typedef struct InstallcoordinationProxyHandle InstallcoordinationProxyHandle; +/** + * Opaque handle to an opened IPSW archive. + */ +typedef struct IpswHandle IpswHandle; + /** * Opaque handle to a ProcessControlClient */ @@ -141,12 +278,34 @@ typedef struct MobileActivationdClientHandle MobileActivationdClientHandle; typedef struct MobileBackup2ClientHandle MobileBackup2ClientHandle; +/** + * Opaque handle to a NetworkMonitorClient + */ +typedef struct NetworkMonitorHandle NetworkMonitorHandle; + typedef struct NotificationProxyClientHandle NotificationProxyClientHandle; +typedef struct NotificationsHandle NotificationsHandle; + typedef struct OsTraceRelayClientHandle OsTraceRelayClientHandle; typedef struct OsTraceRelayReceiverHandle OsTraceRelayReceiverHandle; +/** + * Opaque cancellation token for [`pairable_host_accept`]. + * + * Create one with `pairable_host_cancel_new`, hand it to `pairable_host_accept`, + * and call `pairable_host_cancel_signal` from any other thread to abort the wait. + * Free it with `pairable_host_cancel_free` once the accept has returned. + */ +typedef struct PairableHostCancel PairableHostCancel; + +/** + * Opaque handle holding a generated host identity between + * `pairable_host_prepare` and `pairable_host_accept_fd`. + */ +typedef struct PairableHostHandle PairableHostHandle; + typedef struct PcapdClientHandle PcapdClientHandle; typedef struct PreboardServiceClientHandle PreboardServiceClientHandle; @@ -158,6 +317,21 @@ typedef struct ProcessControlHandle ProcessControlHandle; typedef struct ReadWriteOpaque ReadWriteOpaque; +/** + * Opaque handle to a device in recovery/DFU mode. + */ +typedef struct RecoveryDeviceHandle RecoveryDeviceHandle; + +/** + * Opaque handle to the RemoteXPC-native notification proxy (iOS 17+) + */ +typedef struct RemoteNotificationProxyClientHandle RemoteNotificationProxyClientHandle; + +/** + * Opaque handle to a remote pairing client speaking `RPPairing` over lockdown + */ +typedef struct RemotePairingLockdownHandle RemotePairingLockdownHandle; + /** * Opaque handle to a RemoteServerClient */ @@ -165,6 +339,11 @@ typedef struct RemoteServerHandle RemoteServerHandle; typedef struct RestoreServiceClientHandle RestoreServiceClientHandle; +/** + * Opaque handle to a restore-mode `com.apple.mobile.restored` client. + */ +typedef struct RestoredClientHandle RestoredClientHandle; + /** * Opaque handle to an RPPairing file */ @@ -191,6 +370,11 @@ typedef struct SysdiagnoseStreamHandle SysdiagnoseStreamHandle; typedef struct SyslogRelayClientHandle SyslogRelayClientHandle; +/** + * Opaque handle to a SysmontapClient + */ +typedef struct SysmontapHandle SysmontapHandle; + typedef struct TcpEatObject TcpEatObject; typedef struct TcpFeedObject TcpFeedObject; @@ -205,6 +389,20 @@ typedef struct UsbmuxdListenerHandle UsbmuxdListenerHandle; typedef struct Vec_u64 Vec_u64; +/** + * Opaque handle wrapping a [`WdaBridge`]. + */ +typedef struct WdaBridgeHandle WdaBridgeHandle; + +/** + * Opaque handle wrapping the WDA client state. + * + * The handle owns the provider so that subsequent calls can open fresh + * per-request connections without the caller juggling a separate + * `IdeviceProviderHandle`. + */ +typedef struct WdaClientHandle WdaClientHandle; + typedef struct IdeviceFfiError { int32_t code; int32_t sub_code; @@ -316,16 +514,117 @@ typedef struct SignalResponseC { } SignalResponseC; /** - * C-compatible icon data + * The accessibility color filter's state */ -typedef struct IconDataC { - uint8_t *data; - uintptr_t data_len; - double icon_width; - double icon_height; - double minimum_width; - double minimum_height; -} IconDataC; +typedef struct ColorFilterC { + int enabled; + /** + * The filter preset's name, or NULL if the device didn't report one. + * Free with `idevice_string_free`. + */ + char *filter_type; + /** + * Filter strength, 0.0 to 1.0. Only meaningful when `has_intensity` is 1. + */ + double intensity; + int has_intensity; +} ColorFilterC; + +/** + * A rendered app icon + */ +typedef struct AppIconC { + /** + * PNG-encoded image data + */ + uint8_t *png_data; + uintptr_t png_data_len; + /** + * Icon dimensions in pixels, i.e. the points multiplied by the scale + */ + double pixel_width; + double pixel_height; + /** + * Icon dimensions in points, as actually rendered. May be smaller than + * what was requested. + */ + double width; + double height; + double scale; + /** + * 1 when the device had no real icon for the app and rendered a generic + * placeholder instead + */ + int is_placeholder; +} AppIconC; + +/** + * A cryptex installed on the device + */ +typedef struct InstalledCryptexC { + /** + * Free with `idevice_string_free` + */ + char *identifier; + /** + * Free with `idevice_string_free` + */ + char *version; +} InstalledCryptexC; + +/** + * Which nonce domain a get-nonce or roll-nonce request refers to + */ +typedef struct CryptexNonceDomain { + /** + * When 1, `value` is a nonce domain handle, e.g. a build identity's + * `Cryptex1,NonceDomain`. When 0, it is a domain index, e.g. + * `IDEVICE_CRYPTEXD_NONCE_DOMAIN_CRYPTEX`. + */ + int is_handle; + uint64_t value; +} CryptexNonceDomain; + +/** + * The payloads and parameters one install needs + */ +typedef struct CryptexInstallRequestC { + /** + * The cryptex disk image, i.e. the manifest's `Cryptex1,GenericDmg` + */ + const uint8_t *image; + uintptr_t image_len; + /** + * `Cryptex1,GenericTrustCache` + */ + const uint8_t *trustcache; + uintptr_t trustcache_len; + /** + * The Cryptex1 personalization ticket + */ + const uint8_t *im4m; + uintptr_t im4m_len; + /** + * `Cryptex1,CryptexInfoPlist`, which names and versions the cryptex + */ + const uint8_t *info; + uintptr_t info_len; + /** + * `Cryptex1,GenericVolume` root hash + */ + const uint8_t *volumehash; + uintptr_t volumehash_len; + /** + * The `Cryptex1,*` parameters from the build identity, as a plist + * dictionary. Non-negative integers are sent as uint64, which the daemon + * requires. + */ + plist_t cryptex1_properties; + int64_t image_type_index; + uint64_t persistence; + uint64_t nonce_persistence; + uint64_t auth; +} CryptexInstallRequestC; /** * Represents a debugserver command @@ -336,6 +635,178 @@ typedef struct DebugserverCommandHandle { uintptr_t argv_count; } DebugserverCommandHandle; +/** + * A notification from the mobile notifications instruments channel + */ +typedef struct IdeviceNotificationInfo { + char *notification_type; + int64_t mach_absolute_time; + char *exec_name; + char *app_name; + uint32_t pid; + char *state_description; +} IdeviceNotificationInfo; + +/** + * A single condition profile + */ +typedef struct IdeviceConditionProfile { + char *identifier; + char *description; +} IdeviceConditionProfile; + +/** + * A condition inducer group containing profiles + */ +typedef struct IdeviceConditionGroup { + char *identifier; + struct IdeviceConditionProfile *profiles; + uintptr_t profiles_count; +} IdeviceConditionGroup; + +/** + * A running process on the device + */ +typedef struct IdeviceRunningProcess { + uint32_t pid; + char *name; + char *real_app_name; + bool is_application; + uint64_t start_page_count; +} IdeviceRunningProcess; + +/** + * A parsed per-PID energy sample + */ +typedef struct IdeviceEnergySample { + uint32_t pid; + int64_t timestamp; + double total_energy; + double cpu_energy; + double gpu_energy; + double networking_energy; + double display_energy; + double location_energy; + double appstate_energy; +} IdeviceEnergySample; + +/** + * A graphics sample from tddhe GPU instruments channel + */ +typedef struct IdeviceGraphicsSample { + uint64_t timestamp; + double fps; + uint64_t alloc_system_memory; + uint64_t in_use_system_memory; + uint64_t in_use_system_memory_driver; + char *gpu_bundle_name; + uint64_t recovery_count; +} IdeviceGraphicsSample; + +/** + * A socket address (IPv4 or IPv6), represented as a null-terminated string + port + */ +typedef struct IdeviceSocketAddress { + /** + * Address family (e.g. 2 = AF_INET, 30 = AF_INET6) + */ + uint8_t family; + uint16_t port; + /** + * Null-terminated address string. Must be freed with `idevice_string_free`. + */ + char *addr; +} IdeviceSocketAddress; + +/** + * A network event emitted by the device + */ +typedef struct IdeviceNetworkEvent { + enum IdeviceNetworkEventType event_type; + uint32_t interface_index; + /** + * Null-terminated interface name. Must be freed with `idevice_string_free`. + * Only valid when event_type == InterfaceDetection. + */ + char *interface_name; + struct IdeviceSocketAddress local_addr; + struct IdeviceSocketAddress remote_addr; + /** + * PID of the process owning the connection. Valid for ConnectionDetection. + */ + uint32_t pid; + uint64_t recv_buffer_size; + uint64_t recv_buffer_used; + uint64_t serial_number; + uint32_t kind; + uint64_t rx_packets; + uint64_t rx_bytes; + uint64_t tx_packets; + uint64_t tx_bytes; + uint64_t rx_dups; + uint64_t rx_ooo; + uint64_t tx_retx; + uint64_t min_rtt; + uint64_t avg_rtt; + uint64_t connection_serial; + uint64_t time; + uint64_t unknown_type; +} IdeviceNetworkEvent; + +/** + * Configuration for sysmontap sampling passed over FFI + */ +typedef struct IdeviceSysmontapConfig { + /** + * Sampling interval in milliseconds + */ + uint32_t interval_ms; + /** + * Array of process attribute name strings (null-terminated C strings) + */ + const char *const *process_attributes; + uintptr_t process_attributes_count; + /** + * Array of system attribute name strings (null-terminated C strings) + */ + const char *const *system_attributes; + uintptr_t system_attributes_count; +} IdeviceSysmontapConfig; + +/** + * Progress snapshot passed to `on_progress`. + * + * A session is split into batches of files. `batch_*` describes the batch + * currently streaming; `session_*` accumulates across the whole session. + * Fields are only ever appended to, so a callback compiled against an older + * header stays ABI-compatible. + */ +typedef struct Mobilebackup2BackupProgress { + /** + * Bytes transferred so far in the current batch. + */ + uint64_t batch_bytes_done; + /** + * Bytes the device said this batch contains, or 0 if unknown. Approximate. + */ + uint64_t batch_bytes_total; + /** + * Bytes transferred so far across every batch in this session. Monotonic. + */ + uint64_t session_bytes_done; + /** + * Estimated total bytes for the session, or 0 while not estimable. + * Derived from the device's percentage, so it drifts. Never exact. + */ + uint64_t session_bytes_total; + /** + * Overall progress percentage (0.0-100.0), or negative if not yet known. + * Interpolated within a batch and clamped to be monotonic. Not equal to + * session_bytes_done / session_bytes_total. + */ + double overall_progress; +} Mobilebackup2BackupProgress; + /** * C-compatible delegate for mobilebackup2 operations. * @@ -365,12 +836,16 @@ typedef struct Mobilebackup2BackupDelegateFFI { bool (*exists)(const char *path, void *context); bool (*is_dir)(const char *path, void *context); /** - * Optional progress callback. May be NULL. + * Optional cancellation callback. May be NULL. */ - void (*on_progress)(uint64_t bytes_done, - uint64_t bytes_total, - double overall_progress, - void *context); + bool (*is_cancelled)(void *context); + /** + * Optional progress callback. May be NULL. + * + * `progress` is owned by the caller and only valid for the duration of the + * call; copy out any fields you need to keep. + */ + void (*on_progress)(const struct Mobilebackup2BackupProgress *progress, void *context); } Mobilebackup2BackupDelegateFFI; typedef struct SyslogLabel { @@ -386,8 +861,68 @@ typedef struct OsTraceLog { const char *filename; const char *message; const struct SyslogLabel *label; + /** + * Unique process ID (the activity stream's `procid` field). Equals `pid` + * in practice on iOS. + */ + uint64_t procid; + /** + * ID of the thread that emitted the entry + */ + uint64_t thread_id; + /** + * Load address offset of the log call site within the sender image. Pair + * with `image_uuid` to symbolicate. + */ + uint32_t image_offset; + /** + * UUID of the sender image, i.e. the one named by `image_name` + */ + uint8_t image_uuid[16]; + /** + * UUID of the process' main executable, i.e. the one named by `filename` + */ + uint8_t process_image_uuid[16]; + /** + * Raw monotonic device timestamp in mach ticks + */ + uint64_t mach_timestamp; } OsTraceLog; +/** + * The peer device identity learned during a successful pair-setup. + * + * Free with `rppairing_peer_device_free`. + */ +typedef struct RpPairingPeerDeviceC { + /** + * Peer identifier, the same identifier a later `verifyManualPairing` returns. + */ + char *account_id; + /** + * The device's 16-byte `altIRK`, used to match its mDNS `authTag` records. + */ + uint8_t alt_irk[16]; + /** + * Hardware model identifier, e.g. "AppleTV14,1". + */ + char *model; + /** + * User-visible device name, e.g. "Living Room". + */ + char *name; + /** + * The device's UDID. + */ + char *udid; +} RpPairingPeerDeviceC; + +/** + * Called when the device issues a setup PIN, so the caller can surface it to the + * user. May be NULL. + */ +typedef void (*PairableHostPinCb)(const char *pin, void *context); + /** * Represents a captured device packet from pcapd */ @@ -413,6 +948,164 @@ typedef struct DevicePacketHandle { uintptr_t data_len; } DevicePacketHandle; +/** + * C delegate supplying firmware component bytes by archive path. + * + * `read_component` (required) reads a whole component into a system-allocated + * buffer (ownership transfers to the library, which frees it). The optional + * streaming trio (`open_component`/`read_chunk`/`close_component`) lets large + * source boot objects stream without buffering; when `open_component` is NULL the + * library falls back to buffering via `read_component`. + */ +typedef struct IdeviceRestoreComponentSourceFFI { + void *context; + struct IdeviceFfiError *(*read_component)(const char *path, + uint8_t **out_data, + uintptr_t *out_len, + void *context); + struct IdeviceFfiError *(*open_component)(const char *path, void **out_reader, void *context); + struct IdeviceFfiError *(*read_chunk)(void *reader, + uint8_t *buf, + uintptr_t buf_len, + uintptr_t *out_read, + void *context); + void (*close_component)(void *reader, void *context); +} IdeviceRestoreComponentSourceFFI; + +/** + * C delegate exposing a seekable, sized filesystem (DMG) image for ASR. + */ +typedef struct IdeviceRestoreFilesystemImageFFI { + void *context; + /** + * Returns the total image size in bytes. + */ + struct IdeviceFfiError *(*size)(uint64_t *out_size, void *context); + /** + * Reads up to `len` bytes at `offset` into a system-allocated buffer whose + * ownership transfers to the library. + */ + struct IdeviceFfiError *(*read_at)(uint64_t offset, + uintptr_t len, + uint8_t **out_data, + uintptr_t *out_len, + void *context); +} IdeviceRestoreFilesystemImageFFI; + +/** + * C delegate opening fresh connections to restore-mode data ports. + */ +typedef struct IdeviceRestoreDataPortConnectorFFI { + void *context; + /** + * Connects to `port`, yielding a new [`IdeviceHandle`] (ownership transfers + * to the library). + */ + struct IdeviceFfiError *(*connect)(uint16_t port, + struct IdeviceHandle **out_idevice, + void *context); +} IdeviceRestoreDataPortConnectorFFI; + +/** + * C delegate receiving restore progress callbacks. Any field may be NULL. + */ +typedef struct IdeviceRestoreProgressFFI { + void *context; + /** + * The device's operation code and completion percentage (0–100). + */ + void (*operation)(uint64_t operation, uint64_t progress, void *context); + /** + * A named host step (the `DataType` being serviced). + */ + void (*step)(const char *name, void *context); + void (*transfer)(const char *component, + uint64_t sent, + uint64_t total, + bool has_total, + void *context); +} IdeviceRestoreProgressFFI; + +/** + * C delegate implementing the raw USB surface of a recovery/DFU device. + * + * The library implements the iBoot/DFU protocol on top of these calls, so the + * caller only supplies USB I/O (via nusb, libusb, etc) against the Apple device + * already opened in a recovery/DFU mode. + */ +typedef struct IdeviceRestoreRecoveryTransportFFI { + void *context; + /** + * Host to device control transfer; writes the byte count to `out_transferred`. + */ + struct IdeviceFfiError *(*control_out)(uint8_t request_type, + uint8_t request, + uint16_t value, + uint16_t index, + const uint8_t *data, + uintptr_t data_len, + uint32_t timeout_ms, + uintptr_t *out_transferred, + void *context); + /** + * Device to host control transfer into a system-allocated buffer (ownership + * transfers to the library). + */ + struct IdeviceFfiError *(*control_in)(uint8_t request_type, + uint8_t request, + uint16_t value, + uint16_t index, + uint16_t length, + uint32_t timeout_ms, + uint8_t **out_data, + uintptr_t *out_len, + void *context); + /** + * Bulk OUT transfer; writes the byte count to `out_transferred`. + */ + struct IdeviceFfiError *(*bulk_out)(uint8_t endpoint, + const uint8_t *data, + uintptr_t data_len, + uint32_t timeout_ms, + uintptr_t *out_transferred, + void *context); + /** + * Writes the NUL-terminated USB serial-number string into `buf` + * (capacity `buf_len`). + */ + struct IdeviceFfiError *(*serial_number)(char *buf, uintptr_t buf_len, void *context); + /** + * Returns the device descriptor's `idProduct`. + */ + uint16_t (*product_id)(void *context); + /** + * Selects a configuration. + */ + struct IdeviceFfiError *(*set_configuration)(uint8_t configuration, void *context); + /** + * Claims an interface / alternate setting. + */ + struct IdeviceFfiError *(*claim_interface)(uint8_t iface, uint8_t alt_setting, void *context); + /** + * Resets the device (it re-enumerates afterwards). + */ + struct IdeviceFfiError *(*reset)(void *context); +} IdeviceRestoreRecoveryTransportFFI; + +/** + * C delegate opening FDR trust-channel connections to device ports. + */ +typedef struct IdeviceRestoreFdrConnectorFFI { + void *context; + /** + * Connects to `port`, yielding a new [`IdeviceHandle`] (ownership + * transfers to the library). + */ + struct IdeviceFfiError *(*connect_device_port)(uint16_t port, + struct IdeviceHandle **out_idevice, + void *context); +} IdeviceRestoreFdrConnectorFFI; + /** * C-compatible representation of an RSD service */ @@ -469,6 +1162,22 @@ typedef struct ScreenshotData { uintptr_t length; } ScreenshotData; +/** + * Localhost endpoints exposed by a running WDA bridge. + * + * Pointers in this struct are heap-allocated and must be released with + * `wda_bridge_endpoints_free`. + */ +typedef struct WdaBridgeEndpointsC { + char *udid; + char *wda_url; + char *mjpeg_url; + uint16_t local_http; + uint16_t local_mjpeg; + uint16_t device_http; + uint16_t device_mjpeg; +} WdaBridgeEndpointsC; + /** * Creates a new Idevice connection * @@ -570,6 +1279,14 @@ struct IdeviceFfiError *idevice_start_session(struct IdeviceHandle *idevice, const struct IdevicePairingFile *pairing_file, bool legacy); +/** + * Sets the timeout on async calls such as TCP connections + * + * # Safety + * This function is safe to call from any thread at any time + */ +void idevice_set_global_timeout(uint64_t secs); + /** * Frees an Idevice handle * @@ -1658,16 +2375,47 @@ struct IdeviceFfiError *app_service_send_signal(struct AppServiceHandle *handle, void app_service_free_signal_response(struct SignalResponseC *response); /** - * Fetches an app icon + * Creates a new ConfigurationServiceClient using RSD connection * * # Arguments - * * [`handle`] - The AppServiceClient handle - * * [`bundle_id`] - Bundle identifier of the app - * * [`width`] - Icon width - * * [`height`] - Icon height - * * [`scale`] - Icon scale - * * [`allow_placeholder`] - Whether to allow placeholder icons - * * [`icon_data`] - Pointer to store the icon data (caller must free) + * * [`provider`] - An adapter created by this library + * * [`handshake`] - An RSD handshake from the same provider + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `provider` and `handshake` must be valid pointers to handles allocated by this library + * `handle` must be a valid pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *configuration_service_connect_rsd(struct AdapterHandle *provider, + struct RsdHandshakeHandle *handshake, + struct ConfigurationServiceHandle **handle); + +/** + * Creates a new ConfigurationServiceClient from a socket + * + * # Arguments + * * [`socket`] - The socket to use for communication. Consumed regardless of the result. + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `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 + */ +struct IdeviceFfiError *configuration_service_new(struct ReadWriteOpaque *socket, + struct ConfigurationServiceHandle **handle); + +/** + * Reads the device's light/dark appearance + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`style`] - Pointer to store the appearance * * # Returns * An IdeviceFfiError on error, null on success @@ -1675,21 +2423,234 @@ void app_service_free_signal_response(struct SignalResponseC *response); * # Safety * All pointer parameters must be valid */ -struct IdeviceFfiError *app_service_fetch_app_icon(struct AppServiceHandle *handle, - const char *bundle_id, - float width, - float height, - float scale, - int allow_placeholder, - struct IconDataC **icon_data); +struct IdeviceFfiError *configuration_service_get_user_interface_style(struct ConfigurationServiceHandle *handle, + enum IdeviceUserInterfaceStyle *style); /** - * Frees an IconDataC structure + * Switches the device between light and dark appearance + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`style`] - The appearance to set + * + * # Returns + * An IdeviceFfiError on error, null on success * * # Safety - * `icon_data` must be a valid pointer allocated by app_service_fetch_app_icon + * `handle` must be a valid pointer to a handle allocated by this library */ -void app_service_free_icon_data(struct IconDataC *icon_data); +struct IdeviceFfiError *configuration_service_set_user_interface_style(struct ConfigurationServiceHandle *handle, + enum IdeviceUserInterfaceStyle style); + +/** + * Sets the system liquid-glass opacity + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`opacity`] - The opacity, 0.0 to 1.0 + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *configuration_service_set_liquid_glass_opacity(struct ConfigurationServiceHandle *handle, + float opacity); + +/** + * Reads the accessibility color filter's state + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`filter`] - Pointer to store the state. Free its `filter_type` with + * `idevice_string_free`. + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *configuration_service_get_color_filter(struct ConfigurationServiceHandle *handle, + struct ColorFilterC *filter); + +/** + * Enables or disables the accessibility color filter + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`enabled`] - Whether the filter is on + * * [`filter_type`] - The preset to use, e.g. `Protanopia`. Required when enabling, + * ignored otherwise, and may be NULL when disabling. + * * [`intensity`] - Filter strength, 0.0 to 1.0. Ignored unless `has_intensity` is set. + * * [`has_intensity`] - Whether to send `intensity` + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All non-NULL pointer parameters must be valid + */ +struct IdeviceFfiError *configuration_service_set_color_filter(struct ConfigurationServiceHandle *handle, + int enabled, + const char *filter_type, + float intensity, + int has_intensity); + +/** + * Reads the dynamic-type size's name, e.g. `medium` or `large` + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`size`] - Pointer to store the name. Free with `idevice_string_free`. + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *configuration_service_get_device_text_size(struct ConfigurationServiceHandle *handle, + char **size); + +/** + * Sets the dynamic-type size by name, e.g. `medium` or `large` + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`size`] - The size's name + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *configuration_service_set_device_text_size(struct ConfigurationServiceHandle *handle, + const char *size); + +/** + * Reads whether Reduce Motion is on + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`enabled`] - Pointer to store the state + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *configuration_service_get_reduce_motion(struct ConfigurationServiceHandle *handle, + int *enabled); + +/** + * Toggles Reduce Motion + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`enabled`] - Whether to turn it on + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *configuration_service_set_reduce_motion(struct ConfigurationServiceHandle *handle, + int enabled); + +/** + * Reads whether Reduce Transparency is on + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`enabled`] - Pointer to store the state + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *configuration_service_get_reduce_transparency(struct ConfigurationServiceHandle *handle, + int *enabled); + +/** + * Toggles Reduce Transparency + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`enabled`] - Whether to turn it on + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *configuration_service_set_reduce_transparency(struct ConfigurationServiceHandle *handle, + int enabled); + +/** + * Reads whether the layout-debug borders overlay is on + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`enabled`] - Pointer to store the state + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *configuration_service_get_show_borders(struct ConfigurationServiceHandle *handle, + int *enabled); + +/** + * Toggles the layout-debug borders overlay + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`enabled`] - Whether to turn it on + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *configuration_service_set_show_borders(struct ConfigurationServiceHandle *handle, + int enabled); + +/** + * Toggles Increase Contrast + * + * The device offers no getter for this one. + * + * # Arguments + * * [`handle`] - The ConfigurationServiceClient handle + * * [`enabled`] - Whether to turn it on + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *configuration_service_set_increase_contrast(struct ConfigurationServiceHandle *handle, + int enabled); + +/** + * Frees a ConfigurationServiceClient handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void configuration_service_free(struct ConfigurationServiceHandle *handle); /** * Creates a new DiagnosticsServiceClient using RSD connection @@ -1788,6 +2749,301 @@ void diagnostics_service_free(struct DiagnosticsServiceHandle *handle); */ void sysdiagnose_stream_free(struct SysdiagnoseStreamHandle *handle); +/** + * Creates a new FileServiceClient using RSD connection + * + * This connects the service's control channel, i.e. + * `com.apple.coredevice.fileservice.control`. Downloads additionally need the + * data channel, `com.apple.coredevice.fileservice.data`. + * + * # Arguments + * * [`provider`] - An adapter created by this library + * * [`handshake`] - An RSD handshake from the same provider + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `provider` and `handshake` must be valid pointers to handles allocated by this library + * `handle` must be a valid pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *file_service_connect_rsd(struct AdapterHandle *provider, + struct RsdHandshakeHandle *handshake, + struct FileServiceHandle **handle); + +/** + * Creates a new FileServiceClient from a socket + * + * # Arguments + * * [`socket`] - The socket to use for communication. Consumed regardless of the result. + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `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 + */ +struct IdeviceFfiError *file_service_new(struct ReadWriteOpaque *socket, + struct FileServiceHandle **handle); + +/** + * Opens a session on a domain, which every later command is scoped to + * + * # Arguments + * * [`handle`] - The FileServiceClient handle + * * [`domain`] - The domain to scope the session to + * * [`identifier`] - The container's identifier, i.e. a bundle ID or an app-group ID. + * The domains that don't take one ignore it, and it may be NULL for them. + * * [`session_id`] - Pointer to store the new session's ID, or NULL to ignore it. + * Free with `idevice_string_free`. + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All non-NULL pointer parameters must be valid + */ +struct IdeviceFfiError *file_service_create_session(struct FileServiceHandle *handle, + enum IdeviceFileServiceDomain domain, + const char *identifier, + char **session_id); + +/** + * The session ID from the last `file_service_create_session` + * + * # Arguments + * * [`handle`] - The FileServiceClient handle + * * [`session_id`] - Pointer to store the ID, set to NULL when there is no + * session. Free with `idevice_string_free`. + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *file_service_session_id(struct FileServiceHandle *handle, + char **session_id); + +/** + * Lists a directory, relative to the session's domain root + * + * # Arguments + * * [`handle`] - The FileServiceClient handle + * * [`path`] - The directory to list + * * [`entries`] - Pointer to store the entry names, freed with + * `file_service_free_directory_list` + * * [`len`] - Pointer to store the number of entries + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *file_service_retrieve_directory_list(struct FileServiceHandle *handle, + const char *path, + char ***entries, + uintptr_t *len); + +/** + * Frees the list from `file_service_retrieve_directory_list` + * + * # Safety + * `entries` must be a pointer returned by `file_service_retrieve_directory_list` + * with its reported length, or NULL + */ +void file_service_free_directory_list(char **entries, uintptr_t len); + +/** + * Downloads a file, relative to the session's domain root + * + * The transfer itself runs on the service's data channel, which the caller + * opens by connecting the adapter to the port the RSD handshake reports for + * `com.apple.coredevice.fileservice.data`. + * + * # Arguments + * * [`handle`] - The FileServiceClient handle + * * [`path`] - The file to download + * * [`adapter`] - The adapter the control channel was connected over + * * [`data_port`] - The port of `com.apple.coredevice.fileservice.data` + * * [`data`] - Pointer to store the contents, freed with `idevice_data_free` + * * [`len`] - Pointer to store the number of bytes + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *file_service_retrieve_file(struct FileServiceHandle *handle, + const char *path, + struct AdapterHandle *adapter, + uint16_t data_port, + uint8_t **data, + uintptr_t *len); + +/** + * Downloads a file over a data channel the caller already opened + * + * Like `file_service_retrieve_file`, but takes the data channel itself instead + * of opening one. Note that the device only accepts the connection once the + * control channel has announced the transfer, so a stream opened well in + * advance may have been dropped. + * + * # Arguments + * * [`handle`] - The FileServiceClient handle + * * [`path`] - The file to download + * * [`data_stream`] - The data channel. Consumed regardless of the result. + * * [`data`] - Pointer to store the contents, freed with `idevice_data_free` + * * [`len`] - Pointer to store the number of bytes + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *file_service_retrieve_file_with_stream(struct FileServiceHandle *handle, + const char *path, + struct ReadWriteOpaque *data_stream, + uint8_t **data, + uintptr_t *len); + +/** + * Creates an empty file, relative to the session's domain root + * + * # Arguments + * * [`handle`] - The FileServiceClient handle + * * [`path`] - The file to create + * * [`file_permissions`] - The file's mode, e.g. 0644 + * * [`uid`] - The owning user's ID, e.g. 501 + * * [`gid`] - The owning group's ID, e.g. 501 + * * [`creation_time`] - The creation time to set + * * [`last_modification_time`] - The modification time to set + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *file_service_propose_empty_file(struct FileServiceHandle *handle, + const char *path, + uint32_t file_permissions, + uint32_t uid, + uint32_t gid, + int64_t creation_time, + int64_t last_modification_time); + +/** + * Looks a domain up by the name the device uses, e.g. `appDataContainer` + * + * # Arguments + * * [`name`] - The domain's name + * * [`domain`] - Pointer to store the domain + * + * # Returns + * 1 when the name is known, 0 otherwise + * + * # Safety + * All pointer parameters must be valid + */ +int file_service_domain_from_name(const char *name, enum IdeviceFileServiceDomain *domain); + +/** + * Frees a FileServiceClient handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void file_service_free(struct FileServiceHandle *handle); + +/** + * Creates a new IconServiceClient using RSD connection + * + * # Arguments + * * [`provider`] - An adapter created by this library + * * [`handshake`] - An RSD handshake from the same provider + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `provider` and `handshake` must be valid pointers to handles allocated by this library + * `handle` must be a valid pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *icon_service_connect_rsd(struct AdapterHandle *provider, + struct RsdHandshakeHandle *handshake, + struct IconServiceHandle **handle); + +/** + * Creates a new IconServiceClient from a socket + * + * # Arguments + * * [`socket`] - The socket to use for communication. Consumed regardless of the result. + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `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 + */ +struct IdeviceFfiError *icon_service_new(struct ReadWriteOpaque *socket, + struct IconServiceHandle **handle); + +/** + * Fetches an app's icon, rendered as a PNG + * + * # Arguments + * * [`handle`] - The IconServiceClient handle + * * [`bundle_identifier`] - Bundle identifier of the app, or NULL to use `app_path` + * * [`app_path`] - Path of the app on the device, or NULL to use `bundle_identifier` + * * [`width`] - Requested icon width in points + * * [`height`] - Requested icon height in points + * * [`scale`] - Requested icon scale + * * [`allow_placeholder`] - Whether the device may render a generic placeholder + * * [`icon`] - Pointer to store the icon, freed with `icon_service_free_icon` + * + * Exactly one of `bundle_identifier` and `app_path` must be passed. + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All non-NULL pointer parameters must be valid + */ +struct IdeviceFfiError *icon_service_fetch_icon(struct IconServiceHandle *handle, + const char *bundle_identifier, + const char *app_path, + float width, + float height, + float scale, + int allow_placeholder, + struct AppIconC **icon); + +/** + * Frees an AppIconC + * + * # Safety + * `icon` must be a pointer returned by `icon_service_fetch_icon`, or NULL + */ +void icon_service_free_icon(struct AppIconC *icon); + +/** + * Frees an IconServiceClient handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void icon_service_free(struct IconServiceHandle *handle); + /** * Automatically creates and connects to Core Device Proxy, returning a client handle * @@ -2123,6 +3379,338 @@ struct IdeviceFfiError *crash_report_flush(struct IdeviceProviderHandle *provide */ void crash_report_client_free(struct CrashReportCopyMobileHandle *handle); +/** + * Creates a new CryptexdClient using RSD connection + * + * # Arguments + * * [`provider`] - An adapter created by this library + * * [`handshake`] - An RSD handshake from the same provider + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `provider` and `handshake` must be valid pointers to handles allocated by this library + * `handle` must be a valid pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *cryptexd_connect_rsd(struct AdapterHandle *provider, + struct RsdHandshakeHandle *handshake, + struct CryptexdHandle **handle); + +/** + * Creates a new CryptexdClient from a socket + * + * # Arguments + * * [`socket`] - The socket to use for communication. Consumed regardless of the result. + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `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 + */ +struct IdeviceFfiError *cryptexd_new(struct ReadWriteOpaque *socket, + struct CryptexdHandle **handle); + +/** + * Reads the device's AppleImage4 chip instance, which identifies it in a + * Cryptex1 personalization request + * + * The keys are the daemon's `img4_chip_*` names, e.g. `img4_chip_chip` + * (ChipID), `img4_chip_bord` (BoardID) and `img4_chip_ecid` (ECID). + * + * # Arguments + * * [`handle`] - The CryptexdClient handle. Consumed by this call. + * * [`identifiers`] - Pointer to store the identifiers + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *cryptexd_read_personalization_identifiers(struct CryptexdHandle *handle, + plist_t *identifiers); + +/** + * Lists the cryptexes installed on the device + * + * # Arguments + * * [`handle`] - The CryptexdClient handle. Consumed by this call. + * * [`cryptexes`] - Pointer to store the list, freed with `cryptexd_free_installed` + * * [`len`] - Pointer to store the number of entries + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *cryptexd_copy_installed(struct CryptexdHandle *handle, + struct InstalledCryptexC **cryptexes, + uintptr_t *len); + +/** + * Frees the list from `cryptexd_copy_installed` + * + * # Safety + * `cryptexes` must be a pointer returned by `cryptexd_copy_installed` with its + * reported length, or NULL + */ +void cryptexd_free_installed(struct InstalledCryptexC *cryptexes, uintptr_t len); + +/** + * Frees an InstalledCryptexC allocated by this library + * + * # Safety + * `cryptex` must be a pointer allocated by this library, or NULL + */ +void cryptexd_free_installed_cryptex(struct InstalledCryptexC *cryptex); + +/** + * Reads a nonce domain's nonce structure + * + * Use `cryptexd_cryptex_nonce` for the nonce a TSS request wants. + * + * # Arguments + * * [`handle`] - The CryptexdClient handle. Consumed by this call. + * * [`domain`] - The nonce domain to read + * * [`nonce`] - Pointer to store the nonce, freed with `idevice_data_free` + * * [`nonce_len`] - Pointer to store the number of bytes + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *cryptexd_get_nonce(struct CryptexdHandle *handle, + struct CryptexNonceDomain domain, + uint8_t **nonce, + uintptr_t *nonce_len); + +/** + * Reads the nonce a Cryptex1 TSS request is personalized against + * + * # Arguments + * * [`handle`] - The CryptexdClient handle. Consumed by this call. + * * [`nonce_domain_handle`] - The build identity's `Cryptex1,NonceDomain` + * * [`nonce`] - Pointer to store the nonce, freed with `idevice_data_free` + * * [`nonce_len`] - Pointer to store the number of bytes + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *cryptexd_cryptex_nonce(struct CryptexdHandle *handle, + uint64_t nonce_domain_handle, + uint8_t **nonce, + uintptr_t *nonce_len); + +/** + * Rolls (regenerates) a nonce domain's nonce, invalidating anything + * personalized against the previous one + * + * # Arguments + * * [`handle`] - The CryptexdClient handle. Consumed by this call. + * * [`domain`] - The nonce domain to roll + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *cryptexd_roll_nonce(struct CryptexdHandle *handle, + struct CryptexNonceDomain domain); + +/** + * Uninstalls a cryptex by the identifier `cryptexd_copy_installed` reports + * + * # Arguments + * * [`handle`] - The CryptexdClient handle. Consumed by this call. + * * [`identifier`] - The cryptex's identifier + * * [`version`] - The version to scope the uninstall to, or NULL for all of them + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All non-NULL pointer parameters must be valid + */ +struct IdeviceFfiError *cryptexd_uninstall(struct CryptexdHandle *handle, + const char *identifier, + const char *version); + +/** + * Installs a cryptex + * + * # Arguments + * * [`handle`] - The CryptexdClient handle. Consumed by this call. + * * [`request`] - The payloads and parameters to install + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid, and the request's buffers must be + * readable for their stated lengths + */ +struct IdeviceFfiError *cryptexd_install(struct CryptexdHandle *handle, + const struct CryptexInstallRequestC *request); + +/** + * Extracts the nonce from cryptexd's nonce structure + * + * # Arguments + * * [`blob`] - The structure `cryptexd_get_nonce` returned + * * [`blob_len`] - Its length + * * [`nonce`] - Pointer to store the nonce, freed with `idevice_data_free` + * * [`nonce_len`] - Pointer to store the number of bytes + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid, and `blob` must be readable for + * `blob_len` bytes + */ +struct IdeviceFfiError *cryptexd_unwrap_nonce(const uint8_t *blob, + uintptr_t blob_len, + uint8_t **nonce, + uintptr_t *nonce_len); + +/** + * Loads the DeveloperDiskImage payloads from an unpacked DDI `Restore` directory + * + * # Arguments + * * [`restore_dir`] - The directory to read + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *cryptex1_assets_load(const char *restore_dir, + struct Cryptex1AssetsHandle **handle); + +/** + * Builds the DeveloperDiskImage payloads from buffers the caller already has + * + * # Arguments + * * [`image`] / [`image_len`] - `Cryptex1,GenericDmg` + * * [`trustcache`] / [`trustcache_len`] - `Cryptex1,GenericTrustCache` + * * [`info`] / [`info_len`] - `Cryptex1,CryptexInfoPlist` + * * [`volumehash`] / [`volumehash_len`] - `Cryptex1,GenericVolume` + * * [`build_identity`] - The build identity the payloads came from + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid, and each buffer must be readable for + * its stated length + */ +struct IdeviceFfiError *cryptex1_assets_from_parts(const uint8_t *image, + uintptr_t image_len, + const uint8_t *trustcache, + uintptr_t trustcache_len, + const uint8_t *info, + uintptr_t info_len, + const uint8_t *volumehash, + uintptr_t volumehash_len, + plist_t build_identity, + struct Cryptex1AssetsHandle **handle); + +/** + * The handle of the nonce domain the assets are personalized against, i.e. the + * build identity's `Cryptex1,NonceDomain` + * + * # Arguments + * * [`handle`] - The assets handle + * * [`nonce_domain`] - Pointer to store the handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *cryptex1_assets_nonce_domain(struct Cryptex1AssetsHandle *handle, + uint64_t *nonce_domain); + +/** + * Frees a Cryptex1Assets handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void cryptex1_assets_free(struct Cryptex1AssetsHandle *handle); + +/** + * Personalizes and installs the DeveloperDiskImage cryptex end to end + * + * The cryptex equivalent of the image mounter's auto-mount: reads the device's + * personalization identifiers and cryptex nonce, has Apple sign a Cryptex1 + * ticket for them, and installs the assets. Each step opens its own connection + * off the adapter, since the daemon serves one routine per connection. + * + * # Arguments + * * [`provider`] - An adapter created by this library + * * [`handshake`] - An RSD handshake from the same provider + * * [`assets`] - The payloads to install + * * [`installed`] - Pointer to store the installed cryptex, freed with + * `cryptexd_free_installed_cryptex`. May be NULL to ignore it. + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All non-NULL pointer parameters must be valid + */ +struct IdeviceFfiError *cryptexd_install_ddi(struct AdapterHandle *provider, + struct RsdHandshakeHandle *handshake, + struct Cryptex1AssetsHandle *assets, + struct InstalledCryptexC **installed); + +/** + * The installed DeveloperDiskImage cryptex, if there is one + * + * # Arguments + * * [`provider`] - An adapter created by this library + * * [`handshake`] - An RSD handshake from the same provider + * * [`installed`] - Pointer to store the cryptex, set to NULL when no DDI is + * installed. Freed with `cryptexd_free_installed_cryptex`. + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *cryptexd_installed_ddi(struct AdapterHandle *provider, + struct RsdHandshakeHandle *handshake, + struct InstalledCryptexC **installed); + +/** + * Frees a CryptexdClient handle + * + * Only needed for a handle no routine was invoked on: every routine consumes + * the handle it is passed. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void cryptexd_free(struct CryptexdHandle *handle); + /** * Creates a new DebugserverCommand * @@ -2616,6 +4204,64 @@ struct IdeviceFfiError *location_simulation_set(struct LocationSimulationHandle double latitude, double longitude); +/** + * Frees an IdeviceNotificationInfo and its heap-allocated string fields + * + * # Safety + * `info` must be a valid pointer allocated by this library or NULL + */ +void notifications_info_free(struct IdeviceNotificationInfo *info); + +/** + * Creates a new NotificationsClient from a RemoteServerClient + * + * # Safety + * `server` 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 + */ +struct IdeviceFfiError *notifications_new(struct RemoteServerHandle *server, + struct NotificationsHandle **handle); + +/** + * Frees a NotificationsClient handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void notifications_free(struct NotificationsHandle *handle); + +/** + * Enables application state and memory notifications on the device. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *notifications_start(struct NotificationsHandle *handle); + +/** + * Disables application state and memory notifications on the device. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *notifications_stop(struct NotificationsHandle *handle); + +/** + * Reads the next notification pushed by the device. Blocks until a notification arrives. + * + * # Arguments + * * [`handle`] - The NotificationsClient handle + * * [`info_out`] - On success, set to a heap-allocated IdeviceNotificationInfo + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null. Free the info with `notifications_info_free`. + */ +struct IdeviceFfiError *notifications_get_next(struct NotificationsHandle *handle, + struct IdeviceNotificationInfo **info_out); + /** * Creates a new ProcessControlClient from a RemoteServerClient * @@ -2809,6 +4455,517 @@ struct IdeviceFfiError *screenshot_client_take_screenshot(struct ScreenshotClien uint8_t **data, uintptr_t *len); +/** + * Creates a new ApplicationListingClient from a RemoteServerClient + * + * # Safety + * `server` 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 + */ +struct IdeviceFfiError *application_listing_new(struct RemoteServerHandle *server, + struct ApplicationListingHandle **handle); + +/** + * Frees an ApplicationListingClient handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void application_listing_free(struct ApplicationListingHandle *handle); + +/** + * Returns the list of installed applications as an array of plist dictionaries + * + * # Arguments + * * [`handle`] - The ApplicationListingClient handle + * * [`apps_out`] - On success, set to a heap-allocated array of plist_t values (each is a dict) + * * [`count_out`] - On success, set to the number of apps returned + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null. + * Free the returned array with `idevice_plist_array_free`. + */ +struct IdeviceFfiError *application_listing_get_apps(struct ApplicationListingHandle *handle, + plist_t **apps_out, + uintptr_t *count_out); + +/** + * Creates a new ConditionInducerClient from a RemoteServerClient + * + * # Safety + * `server` 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 + */ +struct IdeviceFfiError *condition_inducer_new(struct RemoteServerHandle *server, + struct ConditionInducerHandle **handle); + +/** + * Frees a ConditionInducerClient handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void condition_inducer_free(struct ConditionInducerHandle *handle); + +/** + * Frees a single IdeviceConditionGroup and all its heap-allocated fields + * + * # Safety + * `group` must be a valid pointer allocated by this library or NULL + */ +void condition_inducer_group_free(struct IdeviceConditionGroup *group); + +/** + * Frees an array of IdeviceConditionGroup pointers + * + * # Safety + * `groups` must be a valid pointer to an array of length `count` allocated by this library, + * or NULL + */ +void condition_inducer_groups_free(struct IdeviceConditionGroup **groups, uintptr_t count); + +/** + * Returns the available condition inducer groups + * + * # Arguments + * * [`handle`] - The ConditionInducerClient handle + * * [`groups_out`] - On success, set to a heap-allocated array of group pointers + * * [`count_out`] - On success, set to the number of groups returned + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null. Free with `condition_inducer_groups_free`. + */ +struct IdeviceFfiError *condition_inducer_available_conditions(struct ConditionInducerHandle *handle, + struct IdeviceConditionGroup ***groups_out, + uintptr_t *count_out); + +/** + * Enables a specific condition profile + * + * # Arguments + * * [`handle`] - The ConditionInducerClient handle + * * [`condition_identifier`] - The condition group identifier (null-terminated C string) + * * [`profile_identifier`] - The profile identifier within the group (null-terminated C string) + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null + */ +struct IdeviceFfiError *condition_inducer_enable(struct ConditionInducerHandle *handle, + const char *condition_identifier, + const char *profile_identifier); + +/** + * Disables the currently active condition + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *condition_inducer_disable(struct ConditionInducerHandle *handle); + +/** + * Creates a new DeviceInfoClient from a RemoteServerClient + * + * # Safety + * `server` 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 + */ +struct IdeviceFfiError *device_info_new(struct RemoteServerHandle *server, + struct DeviceInfoHandle **handle); + +/** + * Frees a DeviceInfoClient handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void device_info_free(struct DeviceInfoHandle *handle); + +/** + * Frees a single IdeviceRunningProcess struct and its heap-allocated strings + * + * # Safety + * `process` must be a valid pointer allocated by this library or NULL + */ +void device_info_running_process_free(struct IdeviceRunningProcess *process); + +/** + * Frees an array of IdeviceRunningProcess pointers + * + * # Safety + * `processes` must be a valid pointer to an array of length `count` allocated by this library, + * or NULL + */ +void device_info_running_processes_free(struct IdeviceRunningProcess **processes, uintptr_t count); + +/** + * Returns the list of running processes on the device + * + * # Arguments + * * [`handle`] - The DeviceInfoClient handle + * * [`processes`] - On success, set to a heap-allocated array of process pointers + * * [`count`] - On success, set to the number of processes returned + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null + */ +struct IdeviceFfiError *device_info_running_processes(struct DeviceInfoHandle *handle, + struct IdeviceRunningProcess ***processes, + uintptr_t *count); + +/** + * Returns the executable name for the given PID + * + * # Safety + * All pointers must be valid and non-null. Free the returned string with `idevice_string_free`. + */ +struct IdeviceFfiError *device_info_execname_for_pid(struct DeviceInfoHandle *handle, + uint32_t pid, + char **name_out); + +/** + * Returns whether the given PID is currently running + * + * # Safety + * All pointers must be valid and non-null + */ +struct IdeviceFfiError *device_info_is_running_pid(struct DeviceInfoHandle *handle, + uint32_t pid, + bool *result); + +/** + * Returns hardware information as a plist dictionary + * + * # Safety + * All pointers must be valid and non-null. Free the returned plist with `plist_free`. + */ +struct IdeviceFfiError *device_info_hardware_information(struct DeviceInfoHandle *handle, + plist_t *plist_out); + +/** + * Returns network information as a plist dictionary + * + * # Safety + * All pointers must be valid and non-null. Free the returned plist with `plist_free`. + */ +struct IdeviceFfiError *device_info_network_information(struct DeviceInfoHandle *handle, + plist_t *plist_out); + +/** + * Returns the mach kernel name + * + * # Safety + * All pointers must be valid and non-null. Free the returned string with `idevice_string_free`. + */ +struct IdeviceFfiError *device_info_mach_kernel_name(struct DeviceInfoHandle *handle, + char **name_out); + +/** + * Frees a null-terminated string array allocated by this library + * + * # Safety + * `strings` must be a valid pointer to an array of `count` C strings allocated by this library, + * or NULL + */ +void device_info_string_array_free(char **strings, uintptr_t count); + +/** + * Returns the list of sysmon process attribute names + * + * # Safety + * All pointers must be valid and non-null. Free with `device_info_string_array_free`. + */ +struct IdeviceFfiError *device_info_sysmon_process_attributes(struct DeviceInfoHandle *handle, + char ***attrs_out, + uintptr_t *count_out); + +/** + * Returns the list of sysmon system attribute names + * + * # Safety + * All pointers must be valid and non-null. Free with `device_info_string_array_free`. + */ +struct IdeviceFfiError *device_info_sysmon_system_attributes(struct DeviceInfoHandle *handle, + char ***attrs_out, + uintptr_t *count_out); + +/** + * Returns directory listing for the given path + * + * # Safety + * All pointers must be valid and non-null. Free with `device_info_string_array_free`. + */ +struct IdeviceFfiError *device_info_directory_listing(struct DeviceInfoHandle *handle, + const char *path, + char ***entries_out, + uintptr_t *count_out); + +/** + * Creates a new EnergyMonitorClient from a RemoteServerClient + * + * # Safety + * `server` 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 + */ +struct IdeviceFfiError *energy_monitor_new(struct RemoteServerHandle *server, + struct EnergyMonitorHandle **handle); + +/** + * Frees an EnergyMonitorClient handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void energy_monitor_free(struct EnergyMonitorHandle *handle); + +/** + * Starts energy sampling for the given PIDs. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library. + * If `pids` is non-null it must point to at least `pids_count` readable `u32` values. + */ +struct IdeviceFfiError *energy_monitor_start_sampling(struct EnergyMonitorHandle *handle, + const uint32_t *pids, + uintptr_t pids_count); + +/** + * Stops energy sampling for the given PIDs. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library. + * If `pids` is non-null it must point to at least `pids_count` readable `u32` values. + */ +struct IdeviceFfiError *energy_monitor_stop_sampling(struct EnergyMonitorHandle *handle, + const uint32_t *pids, + uintptr_t pids_count); + +/** + * Requests a one-shot energy sample and parses the response. + * + * # Arguments + * * [`handle`] - The EnergyMonitorClient handle + * * [`pids`] - Pointer to an array of u32 PIDs to sample + * * [`pids_count`] - Number of elements in `pids` + * * [`samples_out`] - On success, set to a heap-allocated array of IdeviceEnergySample + * * [`samples_count_out`] - On success, set to the number of samples + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All output pointers must be valid and non-null. Free the array with + * `energy_monitor_samples_free`. + */ +struct IdeviceFfiError *energy_monitor_sample_attributes(struct EnergyMonitorHandle *handle, + const uint32_t *pids, + uintptr_t pids_count, + struct IdeviceEnergySample **samples_out, + uintptr_t *samples_count_out); + +/** + * Frees an array of IdeviceEnergySample allocated by `energy_monitor_sample_attributes`. + * + * # Safety + * `samples` must be a pointer returned by this library with the matching `count`, or NULL + */ +void energy_monitor_samples_free(struct IdeviceEnergySample *samples, uintptr_t count); + +/** + * Frees an IdeviceGraphicsSample and its heap-allocated string field + * + * # Safety + * `sample` must be a valid pointer allocated by this library or NULL + */ +void graphics_sample_free(struct IdeviceGraphicsSample *sample); + +/** + * Creates a new GraphicsClient from a RemoteServerClient + * + * # Safety + * `server` 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 + */ +struct IdeviceFfiError *graphics_new(struct RemoteServerHandle *server, + struct GraphicsHandle **handle); + +/** + * Frees a GraphicsClient handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void graphics_free(struct GraphicsHandle *handle); + +/** + * Starts graphics sampling at the given interval. Consumes the device's initial reply internally. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *graphics_start_sampling(struct GraphicsHandle *handle, double interval); + +/** + * Stops graphics sampling. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *graphics_stop_sampling(struct GraphicsHandle *handle); + +/** + * Reads the next graphics data frame pushed by the device. Blocks until a frame arrives. + * + * # Arguments + * * [`handle`] - The GraphicsClient handle + * * [`sample_out`] - On success, set to a heap-allocated IdeviceGraphicsSample + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null. Free the sample with `graphics_sample_free`. + */ +struct IdeviceFfiError *graphics_next_sample(struct GraphicsHandle *handle, + struct IdeviceGraphicsSample **sample_out); + +/** + * Frees an IdeviceNetworkEvent and its heap-allocated string fields + * + * # Safety + * `event` must be a valid pointer allocated by this library or NULL + */ +void network_monitor_event_free(struct IdeviceNetworkEvent *event); + +/** + * Creates a new NetworkMonitorClient from a RemoteServerClient + * + * # Safety + * `server` 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 + */ +struct IdeviceFfiError *network_monitor_new(struct RemoteServerHandle *server, + struct NetworkMonitorHandle **handle); + +/** + * Frees a NetworkMonitorClient handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void network_monitor_free(struct NetworkMonitorHandle *handle); + +/** + * Starts network monitoring. No reply is expected. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *network_monitor_start(struct NetworkMonitorHandle *handle); + +/** + * Stops network monitoring. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *network_monitor_stop(struct NetworkMonitorHandle *handle); + +/** + * Reads the next network event pushed by the device. Blocks until an event arrives. + * + * # Arguments + * * [`handle`] - The NetworkMonitorClient handle + * * [`event_out`] - On success, set to a heap-allocated IdeviceNetworkEvent + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null. Free the event with `network_monitor_event_free`. + */ +struct IdeviceFfiError *network_monitor_next_event(struct NetworkMonitorHandle *handle, + struct IdeviceNetworkEvent **event_out); + +/** + * Creates a new SysmontapClient from a RemoteServerClient + * + * # Safety + * `server` 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 + */ +struct IdeviceFfiError *sysmontap_new(struct RemoteServerHandle *server, + struct SysmontapHandle **handle); + +/** + * Frees a SysmontapClient handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void sysmontap_free(struct SysmontapHandle *handle); + +/** + * Sends configuration to the device + * + * # Arguments + * * [`handle`] - The SysmontapClient handle + * * [`config`] - Pointer to an IdeviceSysmontapConfig struct + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null. String arrays must contain valid C strings. + */ +struct IdeviceFfiError *sysmontap_set_config(struct SysmontapHandle *handle, + const struct IdeviceSysmontapConfig *config); + +/** + * Starts sampling. Consumes the device's initial ack message internally. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *sysmontap_start(struct SysmontapHandle *handle); + +/** + * Stops sampling. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *sysmontap_stop(struct SysmontapHandle *handle); + +/** + * Reads the next sysmontap sample. Blocks until data arrives. + * + * Each output plist is a dictionary (or NULL if that field was not present in the sample): + * - `processes_out`: dict of PID → per-process attribute array + * - `system_out`: plist array of system attribute values + * - `cpu_usage_out`: dict of CPU usage keys + * + * The caller is responsible for freeing non-NULL plists with `plist_free`. + * + * # Safety + * `handle` must be valid and non-null. Output pointers may be null to ignore that field. + */ +struct IdeviceFfiError *sysmontap_next_sample(struct SysmontapHandle *handle, + plist_t *processes_out, + plist_t *system_out, + plist_t *cpu_usage_out); + /** * Frees the IdeviceFfiError * @@ -4113,6 +6270,7 @@ struct IdeviceFfiError *image_mounter_mount_personalized(struct ImageMounterHand * # Returns * An IdeviceFfiError on error, null on success * + * # Safety * All pointers must be valid (except optional ones which can be null) */ struct IdeviceFfiError *image_mounter_mount_personalized_rsd(struct ImageMounterHandle *client, @@ -4544,6 +6702,118 @@ void notification_proxy_free_string(char *s); */ void notification_proxy_client_free(struct NotificationProxyClientHandle *handle); +/** + * Connects to the remote notification proxy over RSD + * + * # Arguments + * * [`provider`] - An adapter created by this library + * * [`handshake`] - An RSD handshake from the same provider + * * [`client`] - On success, will be set to point to a newly allocated handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `provider` and `handshake` must be valid pointers to handles allocated by this library + * `client` must be a valid, non-null pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *remote_notification_proxy_connect_rsd(struct AdapterHandle *provider, + struct RsdHandshakeHandle *handshake, + struct RemoteNotificationProxyClientHandle **client); + +/** + * Creates a remote notification proxy client from a socket + * + * # Arguments + * * [`socket`] - The socket to use for communication. Consumed regardless of the result. + * * [`client`] - On success, will be set to point to a newly allocated handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `socket` must be a valid pointer to a handle allocated by this library + * `client` must be a valid, non-null pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *remote_notification_proxy_new(struct ReadWriteOpaque *socket, + struct RemoteNotificationProxyClientHandle **client); + +/** + * Posts a notification on the device + * + * # Arguments + * * [`client`] - A valid handle + * * [`name`] - The notification to post + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *remote_notification_proxy_post(struct RemoteNotificationProxyClientHandle *client, + const char *name); + +/** + * Registers interest in a notification, after which the device relays it back + * whenever it fires + * + * # Arguments + * * [`client`] - A valid handle + * * [`name`] - The notification to observe + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *remote_notification_proxy_observe(struct RemoteNotificationProxyClientHandle *client, + const char *name); + +/** + * Registers interest in several notifications at once + * + * # Arguments + * * [`client`] - A valid handle + * * [`names`] - The notifications to observe + * * [`len`] - How many notifications were passed + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid, and `names` must hold `len` strings + */ +struct IdeviceFfiError *remote_notification_proxy_observe_multiple(struct RemoteNotificationProxyClientHandle *client, + const char *const *names, + uintptr_t len); + +/** + * Waits for the next relayed notification and returns its name + * + * # Arguments + * * [`client`] - A valid handle + * * [`name_out`] - On success, set to the notification's name. Free with + * `notification_proxy_free_string`. + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *remote_notification_proxy_receive(struct RemoteNotificationProxyClientHandle *client, + char **name_out); + +/** + * Frees a remote notification proxy handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library, or NULL + */ +void remote_notification_proxy_free(struct RemoteNotificationProxyClientHandle *handle); + /** * Connects to the relay with the given provider * @@ -4666,6 +6936,109 @@ struct IdeviceFfiError *os_trace_relay_next(struct OsTraceRelayReceiverHandle *c */ void os_trace_relay_free_log(struct OsTraceLog *log); +/** + * Creates a cancellation token for `pairable_host_accept`. + * + * Returns NULL only if allocation fails. Free with `pairable_host_cancel_free`. + */ +struct PairableHostCancel *pairable_host_cancel_new(void); + +/** + * Signals a cancellation token, unblocking the `pairable_host_accept` it was passed + * to. That call returns the `CanceledByUser` error. + * + * Safe to call from any thread, before or during the accept, and safe to call more + * than once. Cancelling a token that was never passed to an accept, or one whose + * accept already returned, does nothing. + * + * # Safety + * `cancel` must be a pointer returned by `pairable_host_cancel_new` that has not yet + * been freed. + */ +void pairable_host_cancel_signal(const struct PairableHostCancel *cancel); + +/** + * Frees a cancellation token. + * + * The in-flight accept holds its own reference to the shared state, so freeing the + * token while an accept is still running is safe — it just means nothing can cancel + * that accept any more. + * + * # Safety + * `cancel` must be a pointer returned by `pairable_host_cancel_new` or NULL, and must + * not be used afterwards. + */ +void pairable_host_cancel_free(struct PairableHostCancel *cancel); + +/** + * Advertises this computer as a pairable host and accepts a single device-initiated + * pairing. + * + * This blocks the calling thread until a device discovers the advertised + * `_remotepairing-pairable-host._tcp` service, connects, and the pairing either + * completes or fails — or until `cancel` is signalled from another thread. While the + * pairing is in progress `pin_callback` is invoked once with the 6-digit setup code + * that the user must type into the device. + * + * On success a freshly generated [`RpPairingFileHandle`] is written to + * `out_pairing_file`; it carries this host's long-term keys plus the paired + * device's `altIRK`. Persist it (and `out_host_alt_irk`, see below) so the device + * keeps recognizing this host on future connections. + * + * # Arguments + * * `name` - human-readable name shown on the device (e.g. "Jackson's MacBook Pro"). + * * `model` - hardware model identifier shown on the device. `NULL` defaults to + * `"Mac17,7"`. iOS treats the host as a computer, so keep this a Mac identifier. + * * `port` - TCP port to listen on. `0` picks a free port. + * * `pin_callback` - invoked with the setup PIN to display. May be `NULL`. + * * `pin_context` - opaque pointer passed back to `pin_callback`. + * * `cancel` - optional cancellation token from `pairable_host_cancel_new`. Signal it + * from another thread to abort the wait (e.g. the user dismissed the pairing UI). + * `NULL` means the call can only be ended by a device connecting. Without one there + * is no way to stop advertising short of exiting the process. + * * `out_host_alt_irk` - optional. If non-NULL, must point to a 16-byte buffer that + * receives the host's generated `altIRK` (needed to re-advertise this host so an + * already-paired device recognizes it). May be `NULL`. + * * `out_peer_device` - optional. If non-NULL, receives the paired device's identity + * (name, model, UDID, `altIRK`), which the caller must free with + * `rppairing_peer_device_free`. May be `NULL`. + * * `out_pairing_file` - receives the resulting pairing file on success. + * + * # Safety + * `name` must be a valid null-terminated C string. `model` must be NULL or a valid + * null-terminated C string. `cancel` must be NULL or a live token from + * `pairable_host_cancel_new`. `out_host_alt_irk` must be NULL or point to at least 16 + * writable bytes. `out_peer_device` must be NULL or a valid writable pointer. + * `out_pairing_file` must be valid and non-null. + */ +struct IdeviceFfiError *pairable_host_accept(const char *name, + const char *model, + uint16_t port, + void (*pin_callback)(const char *pin, void *context), + void *pin_context, + const struct PairableHostCancel *cancel, + uint8_t *out_host_alt_irk, + struct RpPairingPeerDeviceC **out_peer_device, + struct RpPairingFileHandle **out_pairing_file); + +/** + * Same as `pairable_host_accept`, with explicit pairable-host policy options. + * + * # Safety + * Same pointer validity requirements as `pairable_host_accept`. + */ +struct IdeviceFfiError *pairable_host_accept_with_options(const char *name, + const char *model, + uint16_t port, + bool allows_pinless_pairing, + void (*pin_callback)(const char *pin, + void *context), + void *pin_context, + const struct PairableHostCancel *cancel, + uint8_t *out_host_alt_irk, + struct RpPairingPeerDeviceC **out_peer_device, + struct RpPairingFileHandle **out_pairing_file); + /** * Reads a pairing file from the specified path * @@ -4734,6 +7107,95 @@ struct IdeviceFfiError *idevice_pairing_file_serialize(const struct IdevicePairi */ void idevice_pairing_file_free(struct IdevicePairingFile *pairing_file); +/** + * Generates a fresh host identity and returns the data a caller needs to publish + * its own `_remotepairing-pairable-host._tcp` Bonjour service. + * + * # Arguments + * * `name` - human-readable name shown on the device. + * * `model` - hardware model shown on the device. `NULL` defaults to `"Mac17,7"`. + * * `allows_pinless_pairing` - if true, advertise pinless pairing and use the + * all-zero setup code expected by that flow; if false, generate a random PIN. + * * `out_handle` - receives the host handle; pass it to `pairable_host_accept_fd` + * and free it with `pairable_host_free`. + * * `out_service_id` - receives the Bonjour service instance name. Free with + * `idevice_string_free`. + * * `out_txt_data`/`out_txt_len` - receive an XML plist dictionary of the TXT + * records to publish. Free with `idevice_data_free`. + * * `out_host_alt_irk` - optional. If non-NULL, must point to a 16-byte buffer + * that receives the generated host `altIRK`; persist it with the pairing file. + * + * A fresh identity is generated on every call. + * + * # Safety + * `name` must be a valid null-terminated C string. `model` must be NULL or a + * valid null-terminated C string. All required out-pointers must be valid and + * non-null. `out_host_alt_irk` must be NULL or point to at least 16 writable bytes. + */ +struct IdeviceFfiError *pairable_host_prepare(const char *name, + const char *model, + bool allows_pinless_pairing, + struct PairableHostHandle **out_handle, + char **out_service_id, + uint8_t **out_txt_data, + uintptr_t *out_txt_len, + uint8_t *out_host_alt_irk); + +/** + * Backwards-compatible alias for AltStore's original function name. + * Prefer `pairable_host_prepare` for new callers. + * + * # Safety + * Same requirements as `pairable_host_prepare`, except `model` is required and + * pinless pairing is disabled. + */ +struct IdeviceFfiError *pairable_host_new(const char *name, + const char *model, + struct PairableHostHandle **out_handle, + char **out_service_id, + uint8_t **out_txt_data, + uintptr_t *out_txt_len); + +/** + * Runs pair-setup against a device that has already connected to `fd`. + * + * Blocks the calling thread until pairing succeeds or fails. The fd is duplicated + * before use, so the caller keeps ownership of the original socket. + * + * # Safety + * `handle` must be a valid handle from `pairable_host_prepare` or + * `pairable_host_new`. `fd` must be a valid connected TCP socket. + * `out_pairing_file` must be valid and non-null. `out_peer_device` must be NULL + * or a valid writable pointer. `pin_cb`/`ctx` must stay valid until this call returns. + */ +struct IdeviceFfiError *pairable_host_accept_fd(struct PairableHostHandle *handle, + int32_t fd, + PairableHostPinCb pin_cb, + void *ctx, + struct RpPairingPeerDeviceC **out_peer_device, + struct RpPairingFileHandle **out_pairing_file); + +/** + * Backwards-compatible alias for AltStore's original function name. + * Prefer `pairable_host_accept_fd` for new callers. + * + * # Safety + * Same requirements as `pairable_host_accept_fd`. + */ +struct IdeviceFfiError *pairable_host_handshake(struct PairableHostHandle *handle, + int32_t fd, + PairableHostPinCb pin_cb, + void *ctx, + struct RpPairingFileHandle **out_pairing_file); + +/** + * Frees a `PairableHostHandle`. + * + * # Safety + * `handle` must be a handle from `pairable_host_prepare`/`pairable_host_new`, or NULL. + */ +void pairable_host_free(struct PairableHostHandle *handle); + /** * Automatically creates and connects to pcapd, returning a client handle. * Note that this service only works over USB or through RSD. @@ -4886,12 +7348,14 @@ struct IdeviceFfiError *preboard_service_new(struct IdeviceHandle *socket, struct PreboardServiceClientHandle **client); /** - * Creates a stashbag on the device + * Creates a stashbag on the device from a local preboard manifest (will prompt + * for the passcode on the device), writing the outcome to `out_outcome` * * # Arguments * * `client` - A valid PreboardServiceClient handle * * `manifest` - Pointer to the manifest data * * `manifest_len` - Length of the manifest data + * * `out_outcome` - On success, set to whether a commit is required * * # Returns * An IdeviceFfiError on error, null on success @@ -4899,10 +7363,12 @@ struct IdeviceFfiError *preboard_service_new(struct IdeviceHandle *socket, * # Safety * `client` must be a valid pointer to a handle allocated by this library * `manifest` must be a valid pointer to `manifest_len` bytes of data + * `out_outcome` must be a valid, non-null pointer */ struct IdeviceFfiError *preboard_service_create_stashbag(struct PreboardServiceClientHandle *client, const uint8_t *manifest, - uintptr_t manifest_len); + uintptr_t manifest_len, + enum IdeviceStashbagOutcome *out_outcome); /** * Commits a stashbag on the device @@ -5013,6 +7479,619 @@ struct IdeviceFfiError *usbmuxd_provider_new(struct UsbmuxdAddrHandle *addr, struct IdeviceFfiError *idevice_provider_get_pairing_file(struct IdeviceProviderHandle *provider, struct IdevicePairingFile **pairing_file); +/** + * Connects to `remotepairingdeviced` over lockdown + * + * # Arguments + * * [`provider`] - An IdeviceProvider + * * [`sending_host`] - The name this computer identifies itself by, the same + * value the wireless flow uses + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *remote_pairing_lockdown_connect(struct IdeviceProviderHandle *provider, + const char *sending_host, + struct RemotePairingLockdownHandle **handle); + +/** + * Wraps an existing lockdown connection to `remotepairingdeviced` + * + * # Arguments + * * [`socket`] - A connection to `com.apple.dt.remotepairingdeviced.lockdown`. + * Consumed regardless of the result. + * * [`sending_host`] - The name this computer identifies itself by + * * [`handle`] - Pointer to store the newly created handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *remote_pairing_lockdown_new(struct IdeviceHandle *socket, + const char *sending_host, + struct RemotePairingLockdownHandle **handle); + +/** + * Runs the control channel's handshake and returns what the device reports + * about itself + * + * # Arguments + * * [`handle`] - The client handle + * * [`handshake`] - Pointer to store the device's response + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *remote_pairing_lockdown_attempt_pair_verify(struct RemotePairingLockdownHandle *handle, + plist_t *handshake); + +/** + * Checks whether the device still recognizes a pairing record + * + * The handshake must have run first, i.e. + * `remote_pairing_lockdown_attempt_pair_verify`. + * + * # Arguments + * * [`handle`] - The client handle + * * [`pairing_file`] - The RPPairing file to validate + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *remote_pairing_lockdown_validate_pairing(struct RemotePairingLockdownHandle *handle, + struct RpPairingFileHandle *pairing_file); + +/** + * Pairs with the device, saving the record into `pairing_file` + * + * # Arguments + * * [`handle`] - The client handle + * * [`pairing_file`] - The RPPairing file to pair with, e.g. a fresh one from + * `rp_pairing_file_generate`. Updated in place on success, so write it out + * afterwards to keep the pairing. + * * [`pin`] - The PIN to answer a Trust prompt with, or NULL for `000000`. + * Pairing over USB is promptless, so the device should never ask. + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All non-NULL pointer parameters must be valid + */ +struct IdeviceFfiError *remote_pairing_lockdown_pair(struct RemotePairingLockdownHandle *handle, + struct RpPairingFileHandle *pairing_file, + const char *pin); + +/** + * Pairs only if the device doesn't already recognize the pairing record + * + * Runs the handshake, validates `pairing_file`, and pairs when that fails. + * + * # Arguments + * * [`handle`] - The client handle + * * [`pairing_file`] - The RPPairing file to validate or pair with. Updated in + * place when pairing happens, so write it out afterwards. + * * [`pin`] - The PIN to answer a Trust prompt with, or NULL for `000000` + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All non-NULL pointer parameters must be valid + */ +struct IdeviceFfiError *remote_pairing_lockdown_connect_pairing(struct RemotePairingLockdownHandle *handle, + struct RpPairingFileHandle *pairing_file, + const char *pin); + +/** + * The encryption key established during pairing, used as the TLS-PSK for + * tunnel connections + * + * # Arguments + * * [`handle`] - The client handle + * * [`key`] - Pointer to store the key, freed with `idevice_data_free` + * * [`key_len`] - Pointer to store the number of bytes + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointer parameters must be valid + */ +struct IdeviceFfiError *remote_pairing_lockdown_encryption_key(struct RemotePairingLockdownHandle *handle, + uint8_t **key, + uintptr_t *key_len); + +/** + * Frees a remote pairing lockdown handle + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL + */ +void remote_pairing_lockdown_free(struct RemotePairingLockdownHandle *handle); + +/** + * Connects to `restored` over an existing [`IdeviceHandle`] (consumes it). + * + * # Safety + * `idevice` is consumed and must not be used afterwards. `out_client` must be a + * valid, non-null location for the resulting handle. + */ +struct IdeviceFfiError *idevice_restored_connect(struct IdeviceHandle *idevice, + struct RestoredClientHandle **out_client); + +/** + * Finds a restore-mode device by ECID over usbmux and connects to `restored`. + * + * After a normal to restore transition the device re-enumerates with a new usbmux + * id, so this polls the device list and matches on `HardwareInfo.UniqueChipID`, + * retrying until `timeout_ms` elapses. + * + * # Safety + * `addr` must be a valid `UsbmuxdAddrHandle` (it is borrowed, not consumed); + * `out_client` must be valid; `label` a valid C string or NULL. + */ +struct IdeviceFfiError *idevice_restored_connect_by_ecid(struct UsbmuxdAddrHandle *addr, + uint64_t ecid, + const char *label, + uint64_t timeout_ms, + struct RestoredClientHandle **out_client); + +/** + * Reads the device's ECID (from `HardwareInfo`). + * + * # Safety + * `client` and `out_ecid` must be valid, non-null pointers. + */ +struct IdeviceFfiError *idevice_restored_get_ecid(struct RestoredClientHandle *client, + uint64_t *out_ecid); + +/** + * Reads the usbmux `device_id` the client was found on. + * + * Only meaningful when the client was created with + * `idevice_restored_connect_by_ecid`; writes `true` to `out_has_device_id` in + * that case (and the id to `out_device_id`), or `false` otherwise (e.g. clients + * built from an existing `Idevice`). Pass the id to + * `idevice_restore_connect_usb_port` so data-port / FDR connections target this + * same device. + * + * # Safety + * `client`, `out_device_id`, `out_has_device_id` must be valid, non-null pointers. + */ +struct IdeviceFfiError *idevice_restored_get_device_id(struct RestoredClientHandle *client, + uint32_t *out_device_id, + bool *out_has_device_id); + +/** + * Frees a [`RestoredClientHandle`]. + * + * # Safety + * `client` must be a handle allocated by this library, or NULL. + */ +void idevice_restored_free(struct RestoredClientHandle *client); + +/** + * Connects to `port` on the usbmux device identified by `device_id`, returning a + * new [`IdeviceHandle`]. A convenience for restore data-port and FDR connectors. + * + * `device_id` must be the id `idevice_restored_get_device_id` reported for the + * restore-mode client, so the connection targets the device being restored. + * + * The entire find-device-and-connect sequence runs in one async task; splitting + * it across separate blocking calls corrupts the shared tokio I/O state, so this + * is the supported way to build those connectors from C. + * + * This is a single attempt (the restore state machine retries data-port + * connects itself); it errors rather than blocking when the device or port is + * not yet available. + * + * # Safety + * `addr` must be a valid `UsbmuxdAddrHandle` (it is borrowed, not consumed); + * `out_idevice` must be valid; `label` a valid C string or NULL. + */ +struct IdeviceFfiError *idevice_restore_connect_usb_port(struct UsbmuxdAddrHandle *addr, + uint32_t device_id, + uint16_t port, + const char *label, + struct IdeviceHandle **out_idevice); + +/** + * Allocates a cancellation handle. + * + * # Safety + * `out_handle` must be a valid, non-null location for the handle pointer. + */ +struct IdeviceFfiError *idevice_restore_cancel_handle_new(struct IdeviceRestoreCancelHandle **out_handle); + +/** + * Requests cancellation of the restore this handle was passed to. + * + * Safe to call from any thread while the restore runs; it is a no-op if `handle` + * is NULL. + * + * # Safety + * `handle` must be a valid handle from `idevice_restore_cancel_handle_new` (or NULL). + */ +void idevice_restore_cancel(struct IdeviceRestoreCancelHandle *handle); + +/** + * Frees a cancellation handle. + * + * # Safety + * `handle` must be a valid handle from `idevice_restore_cancel_handle_new` (or NULL) + * and must not be used after this call. + */ +void idevice_restore_cancel_handle_free(struct IdeviceRestoreCancelHandle *handle); + +/** + * Builds the default iOS `RestoreOptions` dictionary sent with `StartRestore`. + * + * The caller may tweak the returned plist before passing it to + * `idevice_restore_run`, and must free it with `plist_free`. + * + * # Safety + * `out_options` must be a valid, non-null location for the plist. + */ +struct IdeviceFfiError *idevice_restore_options_new(plist_t *out_options); + +/** + * Drives the restore-mode state machine to completion. + * + * Sends `StartRestore` with `options`, then services the device's data requests + * (personalizing components with `tss_ticket`, streaming the filesystem over + * ASR, proxying its key requests) until the device reports success. + * + * # Arguments + * * `client` - connected [`RestoredClientHandle`]. + * * `build_identity` - the selected build-identity dictionary (plist). + * * `board_id`, `chip_id`, `ecid` - device identifiers. + * * `tss_ticket`/`tss_ticket_len` - the `ApImg4Ticket` (IM4M) from TSS. + * * `components` - component-source delegate (required). + * * `filesystem` - filesystem-image delegate, or NULL for a restore that sends + * no filesystem. + * * `data_ports` - data-port connector delegate (required). + * * `progress` - progress delegate, or NULL. + * * `cancel` - cancellation handle from `idevice_restore_cancel_handle_new`, or + * NULL. When another thread calls `idevice_restore_cancel` on it, the restore + * stops and the device is rebooted toward recovery (returning a `Cancelled` + * error). + * * `options` - the `RestoreOptions` plist (see `idevice_restore_options_new`). + * + * # Safety + * All non-NULL pointers must be valid for the duration of the call, and each + * delegate struct must remain valid until this returns. + */ +struct IdeviceFfiError *idevice_restore_run(struct RestoredClientHandle *client, + plist_t build_identity, + uint64_t board_id, + uint64_t chip_id, + uint64_t ecid, + const uint8_t *tss_ticket, + uintptr_t tss_ticket_len, + struct IdeviceRestoreComponentSourceFFI *components, + struct IdeviceRestoreFilesystemImageFFI *filesystem, + struct IdeviceRestoreDataPortConnectorFFI *data_ports, + struct IdeviceRestoreProgressFFI *progress, + struct IdeviceRestoreCancelHandle *cancel, + plist_t options); + +/** + * Opens an IPSW archive from a filesystem path. + * + * # Safety + * `path` must be a valid C string; `out_ipsw` a valid, non-null location. + */ +struct IdeviceFfiError *idevice_ipsw_open(const char *path, struct IpswHandle **out_ipsw); + +/** + * Reads and parses the archive's `BuildManifest.plist`. + * + * # Safety + * `ipsw` must be a valid handle; `out_manifest` a valid, non-null location. The + * returned plist must be freed with `plist_free`. + */ +struct IdeviceFfiError *idevice_ipsw_build_manifest(struct IpswHandle *ipsw, plist_t *out_manifest); + +/** + * Reads a component named in `build_identity` into a caller-freed buffer. + * + * # Safety + * `ipsw`, `name`, `out_data`, `out_len` must be valid. Free the buffer with + * `idevice_data_free`. + */ +struct IdeviceFfiError *idevice_ipsw_read_component(struct IpswHandle *ipsw, + plist_t build_identity, + const char *name, + uint8_t **out_data, + uintptr_t *out_len); + +/** + * Reads an arbitrary archive entry by exact path into a caller-freed buffer. + * + * # Safety + * `ipsw`, `path`, `out_data`, `out_len` must be valid. Free with + * `idevice_data_free`. + */ +struct IdeviceFfiError *idevice_ipsw_read_file(struct IpswHandle *ipsw, + const char *path, + uint8_t **out_data, + uintptr_t *out_len); + +/** + * Extracts an archive entry to a file on disk (streamed, for large images). + * + * # Safety + * `ipsw`, `entry_path`, `dest_path` must be valid C strings. + */ +struct IdeviceFfiError *idevice_ipsw_extract_to_file(struct IpswHandle *ipsw, + const char *entry_path, + const char *dest_path); + +/** + * Frees an [`IpswHandle`]. + * + * # Safety + * `ipsw` must be a handle allocated by this library, or NULL. + */ +void idevice_ipsw_free(struct IpswHandle *ipsw); + +/** + * Selects the `BuildIdentity` matching `board_id`/`chip_id` (and, when non-NULL, + * `restore_behavior`, e.g. "Erase"/"Update") from a `BuildManifest` plist. + * + * # Safety + * `build_manifest`, `out_identity` must be valid. The result plist must be freed + * with `plist_free`. + */ +struct IdeviceFfiError *idevice_restore_select_build_identity(plist_t build_manifest, + uint64_t board_id, + uint64_t chip_id, + const char *restore_behavior, + plist_t *out_identity); + +/** + * Resolves the archive path of a component from a build identity's `Manifest`. + * + * # Safety + * `build_identity`, `name`, `out_path` must be valid. Free the string with + * `idevice_string_free`. + */ +struct IdeviceFfiError *idevice_restore_component_path(plist_t build_identity, + const char *name, + char **out_path); + +/** + * Fetches the AP `ApImg4Ticket` (IM4M) from Apple's TSS server for a build + * identity and device, returning the ticket bytes. + * + * `ap_nonce`/`sep_nonce` may be NULL (length 0); a NULL `sep_nonce` is signed + * with a zeroed nonce. + * + * # Safety + * `build_identity`, `out_ticket`, `out_ticket_len` must be valid. Free the ticket + * with `idevice_data_free`. + */ +struct IdeviceFfiError *idevice_restore_fetch_ap_ticket(plist_t build_identity, + uint64_t board_id, + uint64_t chip_id, + uint64_t ecid, + const uint8_t *ap_nonce, + uintptr_t ap_nonce_len, + const uint8_t *sep_nonce, + uintptr_t sep_nonce_len, + uint8_t **out_ticket, + uintptr_t *out_ticket_len); + +/** + * Stitches an `IM4P` component with the `ApImg4Ticket` into a personalized + * `IMG4` the device will accept. + * + * `fourcc` is either NULL (keep the payload's own type) or a pointer to exactly + * four bytes to re-tag the payload with (required for some `Restore*` components; + * see the library's `restore_fourcc_override`). + * + * # Safety + * `im4p`, `ticket`, `out_data`, `out_len` must be valid. If non-NULL, `fourcc` + * must point to 4 readable bytes. Free the buffer with `idevice_data_free`. + */ +struct IdeviceFfiError *idevice_img4_stitch_component(const uint8_t *im4p, + uintptr_t im4p_len, + const uint8_t *ticket, + uintptr_t ticket_len, + const uint8_t *fourcc, + uint8_t **out_data, + uintptr_t *out_len); + +/** + * Returns the four-character code a `Restore*` component must be re-tagged with, + * if any, writing four bytes to `out_fourcc`. + * + * Returns `true` and fills `out_fourcc` when the component needs re-tagging; + * returns `false` and leaves `out_fourcc` untouched otherwise. + * + * # Safety + * `component_name` must be a valid C string; `out_fourcc` must point to 4 + * writable bytes. + */ +bool idevice_img4_restore_fourcc_override(const char *component_name, uint8_t *out_fourcc); + +/** + * Returns the components iBoot loads during the restore boot, in manifest order, + * as a newline-separated, NUL-terminated string (empty when none). + * + * # Safety + * `build_identity`, `out_names` must be valid. Free the string with + * `idevice_string_free`. + */ +struct IdeviceFfiError *idevice_restore_boot_component_names(plist_t build_identity, + char **out_names); + +/** + * Builds the local (unsigned) `IM4M` preboard manifest for a stashbag request + * from a build identity, into a caller-freed buffer. + * + * # Safety + * `build_identity`, `out_data`, `out_len` must be valid. Free the buffer with + * `idevice_data_free`. + */ +struct IdeviceFfiError *idevice_restore_build_preboard_manifest(plist_t build_identity, + uint64_t board_id, + uint64_t chip_id, + uint8_t **out_data, + uintptr_t *out_len); + +/** + * Opens a recovery/DFU device over a caller-supplied transport delegate. + * + * The `transport` struct is copied by value; the caller may free its own + * storage after this returns (the `context` pointer must stay valid). + * + * # Safety + * `transport` and `out_device` must be valid, non-null pointers. + */ +struct IdeviceFfiError *idevice_recovery_device_new(const struct IdeviceRestoreRecoveryTransportFFI *transport, + struct RecoveryDeviceHandle **out_device); + +/** + * Sends an iBoot command (NUL-terminated), with an explicit `bRequest`. + * + * # Safety + * `device`, `command` must be valid. + */ +struct IdeviceFfiError *idevice_recovery_send_command(struct RecoveryDeviceHandle *device, + const char *command, + uint8_t b_request); + +/** + * Uploads a firmware image (bulk in recovery mode, chunked control transfers + * in DFU mode). + * + * # Safety + * `device`, `data` must be valid. + */ +struct IdeviceFfiError *idevice_recovery_send_buffer(struct RecoveryDeviceHandle *device, + const uint8_t *data, + uintptr_t len); + +/** + * Reads an environment variable via `getenv` into a caller-freed buffer. + * + * # Safety + * `device`, `name`, `out_data`, `out_len` must be valid. Free with + * `idevice_data_free`. + */ +struct IdeviceFfiError *idevice_recovery_getenv(struct RecoveryDeviceHandle *device, + const char *name, + uint8_t **out_data, + uintptr_t *out_len); + +/** + * Sets an environment variable via `setenv`. + * + * # Safety + * `device`, `name`, `value` must be valid. + */ +struct IdeviceFfiError *idevice_recovery_setenv(struct RecoveryDeviceHandle *device, + const char *name, + const char *value); + +/** + * Enables or disables auto-boot and persists it (`saveenv`). + * + * # Safety + * `device` must be valid. + */ +struct IdeviceFfiError *idevice_recovery_set_autoboot(struct RecoveryDeviceHandle *device, + bool enable); + +/** + * Issues the zero-length `finish_transfer` control request. + * + * # Safety + * `device` must be valid. + */ +struct IdeviceFfiError *idevice_recovery_finish_transfer(struct RecoveryDeviceHandle *device); + +/** + * Reboots the device. + * + * # Safety + * `device` must be valid. + */ +struct IdeviceFfiError *idevice_recovery_reboot(struct RecoveryDeviceHandle *device); + +/** + * Returns the device's USB `idProduct` (identifying its mode), and whether it + * is a recovery (iBoot) mode as opposed to DFU/WTF. + * + * # Safety + * `device`, `out_product_id`, `out_is_recovery` must be valid. + */ +struct IdeviceFfiError *idevice_recovery_get_mode(struct RecoveryDeviceHandle *device, + uint16_t *out_product_id, + bool *out_is_recovery); + +/** + * Fills device identifiers parsed from the recovery serial string. + * + * Each `has_*` output is set to whether the corresponding value was present; + * missing values leave their `out_*` untouched. + * + * # Safety + * All non-null out-pointers must be valid. + */ +struct IdeviceFfiError *idevice_recovery_get_info(struct RecoveryDeviceHandle *device, + uint64_t *out_cpid, + bool *out_has_cpid, + uint64_t *out_bdid, + bool *out_has_bdid, + uint64_t *out_ecid, + bool *out_has_ecid); + +/** + * Returns the AP nonce (`NONC`) from the recovery serial, if present, into a + * caller-freed buffer. Returns `true` when a nonce was present. + * + * # Safety + * `device`, `out_data`, `out_len` must be valid. Free with `idevice_data_free`. + */ +bool idevice_recovery_get_ap_nonce(struct RecoveryDeviceHandle *device, + uint8_t **out_data, + uintptr_t *out_len); + +/** + * Frees a [`RecoveryDeviceHandle`]. + * + * # Safety + * `device` must be a handle allocated by this library, or NULL. + */ +void idevice_recovery_device_free(struct RecoveryDeviceHandle *device); + +/** + * Starts the FDR trust channel: control handshake, then a background listener + * running for the rest of the restore. + * + * The `connector` struct is copied by value (its `context` must stay valid for + * the duration of the restore). + * + * # Safety + * `connector` must be a valid, non-null pointer. + */ +struct IdeviceFfiError *idevice_restore_fdr_start(const struct IdeviceRestoreFdrConnectorFFI *connector); + /** * Creates a new RestoreServiceClient from a ReadWrite stream * @@ -5214,6 +8293,15 @@ struct IdeviceFfiError *rp_pairing_file_write(struct RpPairingFileHandle *handle */ void rp_pairing_file_free(struct RpPairingFileHandle *handle); +/** + * Frees a peer device struct and its heap-allocated string fields. + * + * # Safety + * `peer_device` must be a pointer returned by `rppairing_pair_network` or + * `pairable_host_accept`, or NULL. + */ +void rppairing_peer_device_free(struct RpPairingPeerDeviceC *peer_device); + /** * Creates a new RSD handshake from a ReadWrite connection * @@ -5768,8 +8856,12 @@ struct IdeviceFfiError *tunnel_create_remotexpc(const idevice_sockaddr *addr, * Use this when connecting to a device discovered via `_remotepairing._tcp`. * The connection goes: direct TCP → RPPairing (JSON) → tunnel. * - * This path only supports pair-verify (existing pairing file required). - * For initial pairing, use `tunnel_pair_usb`. + * `pairing_file` is used for pair-verify. If verification fails (typically + * because the device has never been paired with this host) a full pair-setup + * runs on the same connection and `pairing_file` is updated in place, so the + * caller should persist it afterwards regardless of whether it was freshly + * generated. + * * * # Safety * All pointer arguments must be valid and non-null (except `pin_callback`/`pin_context`). @@ -5784,6 +8876,38 @@ struct IdeviceFfiError *tunnel_create_rppairing(const idevice_sockaddr *addr, struct AdapterHandle **out_adapter, struct RsdHandshakeHandle **out_handshake); +/** + * Pairs with a device over the network via raw RPPairing, without creating a tunnel. + * + * This is for tvOS. + * + * On iOS `tunnel_create_rppairing` handles both halves on its own; this function + * is only needed there if you want to pair and connect as separate steps. + * + * # Arguments + * * `addr` / `addr_len` - address of the pairing service to connect to. + * * `hostname` - name this host presents to the device. + * * `pairing_file` - borrowed, not consumed. Updated in place on success. Pass a + * freshly generated file (`rp_pairing_file_generate`) for a first-time pairing. + * * `pin_callback` / `pin_context` - invoked to obtain the PIN shown on the + * device. May be `NULL`. + * * `out_peer_device` - optional. If non-NULL, receives the paired device's + * identity, which the caller must free with `rppairing_peer_device_free`. Only + * written when a pair-setup actually ran; a successful pair-verify leaves it + * NULL. + * + * # Safety + * All pointer arguments must be valid and non-null except `pin_callback`, + * `pin_context`, and `out_peer_device`. + */ +struct IdeviceFfiError *rppairing_pair_network(const idevice_sockaddr *addr, + idevice_socklen_t addr_len, + const char *hostname, + struct RpPairingFileHandle *pairing_file, + const char *(*pin_callback)(void *context), + void *pin_context, + struct RpPairingPeerDeviceC **out_peer_device); + /** * Connects to a usbmuxd instance over TCP * @@ -6120,6 +9244,543 @@ uint32_t idevice_usbmuxd_device_get_device_id(const struct UsbmuxdDeviceHandle * */ uint8_t idevice_usbmuxd_device_get_connection_type(const struct UsbmuxdDeviceHandle *device); +/** + * Creates a new WDA client bound to the given provider. + * + * # Arguments + * * [`provider`] - An IdeviceProvider. The provider is consumed and may not + * be used again, regardless of whether this call succeeds or fails. + * * [`handle`] - On success, set to a newly allocated WdaClientHandle. + * + * # Returns + * An IdeviceFfiError on error, null on success. + * + * # Safety + * `provider` must be a valid pointer to a handle allocated by this library. + * The provider is consumed, and may not be used again. + * `handle` must be a valid, non-null pointer to a location where the handle will be stored. + */ +struct IdeviceFfiError *wda_client_new(struct IdeviceProviderHandle *provider, + struct WdaClientHandle **handle); + +/** + * Frees a WDA client handle. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library or NULL. + */ +void wda_client_free(struct WdaClientHandle *handle); + +/** + * Sets the device-side WDA HTTP and MJPEG ports. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library. + */ +struct IdeviceFfiError *wda_client_set_ports(struct WdaClientHandle *handle, + uint16_t http, + uint16_t mjpeg); + +/** + * Sets the per-request timeout in milliseconds. + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library. + */ +struct IdeviceFfiError *wda_client_set_timeout_ms(struct WdaClientHandle *handle, uint64_t ms); + +/** + * Reads the configured device-side WDA ports. + * + * # Safety + * All pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_get_ports(struct WdaClientHandle *handle, + uint16_t *out_http, + uint16_t *out_mjpeg); + +/** + * Returns the currently tracked session id, or NULL if none. + * + * # Arguments + * * [`handle`] - The WDA client handle. + * * [`out_str`] - On success, set to a heap-allocated UTF-8 string, or NULL + * if no session is tracked. Free with `idevice_string_free` if non-null. + * + * # Safety + * All pointers must be valid; `out_str` must be non-null. + */ +struct IdeviceFfiError *wda_client_session_id(struct WdaClientHandle *handle, char **out_str); + +/** + * Fetches `/status` from the WDA HTTP endpoint and returns the JSON response. + * + * # Arguments + * * [`handle`] - The WDA client handle. + * * [`out_json`] - On success, set to a heap-allocated JSON string. Free with + * `idevice_string_free`. + * + * # Safety + * All pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_status(struct WdaClientHandle *handle, char **out_json); + +/** + * Waits until WDA begins responding on its HTTP endpoint. + * + * # Safety + * All pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_wait_until_ready(struct WdaClientHandle *handle, + uint64_t timeout_ms, + char **out_json); + +/** + * Starts a WDA session and stores the resulting session id on the handle. + * + * # Arguments + * * [`handle`] - The WDA client handle. + * * [`bundle_id`] - Optional bundle identifier; pass NULL for an anonymous session. + * * [`out_session_id`] - On success, set to a heap-allocated UTF-8 string. + * Free with `idevice_string_free`. + * + * # Safety + * `handle` and `out_session_id` must be valid and non-null. `bundle_id` may be NULL. + */ +struct IdeviceFfiError *wda_client_start_session(struct WdaClientHandle *handle, + const char *bundle_id, + char **out_session_id); + +/** + * Deletes a WDA session. + * + * # Safety + * `handle` and `session_id` must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_delete_session(struct WdaClientHandle *handle, + const char *session_id); + +/** + * Finds a single element and returns its WDA element id. + * + * # Safety + * `handle`, `using`, `value`, and `out_element_id` must be valid and non-null. + * `session_id` may be NULL to use the handle's tracked session. + */ +struct IdeviceFfiError *wda_client_find_element(struct WdaClientHandle *handle, + const char *using_, + const char *value, + const char *session_id, + char **out_element_id); + +/** + * Finds multiple elements and returns their WDA element ids. + * + * # Arguments + * * [`out_array`] - On success, set to a heap-allocated array of NUL-terminated strings. + * * [`out_count`] - On success, set to the number of strings. + * + * Free the array with `wda_client_string_array_free`. + * + * # Safety + * All non-optional pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_find_elements(struct WdaClientHandle *handle, + const char *using_, + const char *value, + const char *session_id, + char ***out_array, + uintptr_t *out_count); + +/** + * Frees an array of strings allocated by `wda_client_find_elements`. + * + * # Safety + * `arr` must be a pointer returned by `wda_client_find_elements` with the + * matching `count`, or NULL. + */ +void wda_client_string_array_free(char **arr, uintptr_t count); + +/** + * Returns a raw attribute value as a JSON-encoded string. + * + * # Safety + * All non-optional pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_element_attribute(struct WdaClientHandle *handle, + const char *element_id, + const char *name, + const char *session_id, + char **out_json); + +/** + * Returns the element text-like value as a string. + * + * # Safety + * All non-optional pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_element_text(struct WdaClientHandle *handle, + const char *element_id, + const char *session_id, + char **out_str); + +/** + * Returns the element bounds rectangle as a JSON-encoded string. + * + * # Safety + * All non-optional pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_element_rect(struct WdaClientHandle *handle, + const char *element_id, + const char *session_id, + char **out_json); + +/** + * Returns whether an element is displayed. + * + * # Safety + * All non-optional pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_element_displayed(struct WdaClientHandle *handle, + const char *element_id, + const char *session_id, + bool *out_bool); + +/** + * Returns whether an element is enabled. + * + * # Safety + * All non-optional pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_element_enabled(struct WdaClientHandle *handle, + const char *element_id, + const char *session_id, + bool *out_bool); + +/** + * Returns whether an element is selected. + * + * # Safety + * All non-optional pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_element_selected(struct WdaClientHandle *handle, + const char *element_id, + const char *session_id, + bool *out_bool); + +/** + * Clicks an element by its WDA element id. + * + * # Safety + * `handle` and `element_id` must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_click(struct WdaClientHandle *handle, + const char *element_id, + const char *session_id); + +/** + * Sends text input to the currently focused element. + * + * # Safety + * `handle` and `text` must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_send_keys(struct WdaClientHandle *handle, + const char *text, + const char *session_id); + +/** + * Presses a hardware button through WDA. + * + * # Safety + * `handle` and `name` must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_press_button(struct WdaClientHandle *handle, + const char *name, + const char *session_id); + +/** + * Unlocks the device via WDA. + * + * # Safety + * `handle` must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_unlock(struct WdaClientHandle *handle, const char *session_id); + +/** + * Swipes from one coordinate to another. + * + * # Safety + * `handle` must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_swipe(struct WdaClientHandle *handle, + int64_t start_x, + int64_t start_y, + int64_t end_x, + int64_t end_y, + double duration, + const char *session_id); + +/** + * Performs a tap gesture. + * + * `Option` arguments are encoded as `(has, value)` pairs. When `has_*` + * is false the underlying value is ignored. + * + * # Safety + * `handle` must be valid and non-null. Optional pointers may be NULL. + */ +struct IdeviceFfiError *wda_client_tap(struct WdaClientHandle *handle, + bool has_x, + double x, + bool has_y, + double y, + const char *element_id, + const char *session_id); + +/** + * Performs a double-tap gesture. + * + * # Safety + * `handle` must be valid and non-null. Optional pointers may be NULL. + */ +struct IdeviceFfiError *wda_client_double_tap(struct WdaClientHandle *handle, + bool has_x, + double x, + bool has_y, + double y, + const char *element_id, + const char *session_id); + +/** + * Performs a long-press gesture. + * + * # Safety + * `handle` must be valid and non-null. Optional pointers may be NULL. + */ +struct IdeviceFfiError *wda_client_touch_and_hold(struct WdaClientHandle *handle, + double duration, + bool has_x, + double x, + bool has_y, + double y, + const char *element_id, + const char *session_id); + +/** + * Scrolls the current view or an element using a WDA mobile command. + * + * `Option` arguments are encoded as `(has, value)`. + * + * # Safety + * `handle` must be valid and non-null. Optional string arguments may be NULL. + */ +struct IdeviceFfiError *wda_client_scroll(struct WdaClientHandle *handle, + const char *direction, + const char *name, + const char *predicate_string, + bool has_to_visible, + bool to_visible, + const char *element_id, + const char *session_id); + +/** + * Returns the current UI source tree as XML. + * + * # Safety + * `handle` and `out_str` must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_source(struct WdaClientHandle *handle, + const char *session_id, + char **out_str); + +/** + * Returns a PNG screenshot as raw bytes. + * + * # Arguments + * * [`out_bytes`] - On success, set to a heap-allocated PNG buffer. + * * [`out_len`] - On success, set to the buffer length in bytes. + * + * Free the buffer with `idevice_data_free`. + * + * # Safety + * All non-optional pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_screenshot(struct WdaClientHandle *handle, + const char *session_id, + uint8_t **out_bytes, + uintptr_t *out_len); + +/** + * Returns the current window size payload from WDA. + * + * # Safety + * All non-optional pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_window_size(struct WdaClientHandle *handle, + const char *session_id, + char **out_json); + +/** + * Returns the current viewport rectangle. + * + * # Safety + * All non-optional pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_viewport_rect(struct WdaClientHandle *handle, + const char *session_id, + char **out_json); + +/** + * Returns the current orientation as a string. + * + * # Safety + * All non-optional pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_orientation(struct WdaClientHandle *handle, + const char *session_id, + char **out_str); + +/** + * Launches or activates an application via WDA. + * + * # Arguments + * * [`bundle_id`] - The bundle identifier of the app to launch. + * * [`arguments`] - Optional array of argument strings; pass NULL for none. + * * [`arguments_count`] - Number of strings in `arguments`; ignored if NULL. + * * [`environment_json`] - Optional JSON object string of environment variables; pass NULL for none. + * + * # Safety + * `handle`, `bundle_id`, and `out_json` must be valid and non-null. Optional + * pointers may be NULL. + */ +struct IdeviceFfiError *wda_client_launch_app(struct WdaClientHandle *handle, + const char *bundle_id, + const char *const *arguments, + uintptr_t arguments_count, + const char *environment_json, + const char *session_id, + char **out_json); + +/** + * Activates an already running application. + * + * # Safety + * `handle`, `bundle_id`, and `out_json` must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_activate_app(struct WdaClientHandle *handle, + const char *bundle_id, + const char *session_id, + char **out_json); + +/** + * Terminates an application and returns whether termination succeeded. + * + * # Safety + * `handle`, `bundle_id`, and `out_bool` must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_terminate_app(struct WdaClientHandle *handle, + const char *bundle_id, + const char *session_id, + bool *out_bool); + +/** + * Queries the XCTest application state for the given bundle id. + * + * # Safety + * `handle`, `bundle_id`, and `out_state` must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_query_app_state(struct WdaClientHandle *handle, + const char *bundle_id, + const char *session_id, + int64_t *out_state); + +/** + * Backgrounds the current app for the given number of seconds. + * + * `Option` is encoded as `(has_seconds, seconds)`. + * + * # Safety + * `handle` and `out_json` must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_background_app(struct WdaClientHandle *handle, + bool has_seconds, + double seconds, + const char *session_id, + char **out_json); + +/** + * Returns whether the device is currently locked. + * + * # Safety + * `handle` and `out_bool` must be valid and non-null. + */ +struct IdeviceFfiError *wda_client_is_locked(struct WdaClientHandle *handle, + const char *session_id, + bool *out_bool); + +/** + * Starts a localhost bridge to the device's default WDA ports. + * + * # Arguments + * * [`provider`] - An IdeviceProvider. Provider ownership is transferred — + * the caller must not free or reuse the IdeviceProviderHandle on success or failure. + * * [`handle`] - On success, set to a newly allocated WdaBridgeHandle. + * + * # Returns + * An IdeviceFfiError on error, null on success. + * + * # Safety + * `provider` must be a valid pointer to a handle allocated by this library. + * The provider is consumed, and may not be used again. + * `handle` must be a valid, non-null pointer to a location where the handle will be stored. + */ +struct IdeviceFfiError *wda_bridge_start(struct IdeviceProviderHandle *provider, + struct WdaBridgeHandle **handle); + +/** + * Starts a localhost bridge to custom device-side WDA ports. + * + * # Safety + * Same requirements as [`wda_bridge_start`]. + */ +struct IdeviceFfiError *wda_bridge_start_with_ports(struct IdeviceProviderHandle *provider, + uint16_t device_http, + uint16_t device_mjpeg, + struct WdaBridgeHandle **handle); + +/** + * Reads the endpoints assigned to the running bridge. + * + * # Arguments + * * [`handle`] - The bridge handle. + * * [`out_endpoints`] - On success, set to a heap-allocated WdaBridgeEndpointsC. + * Free with `wda_bridge_endpoints_free`. + * + * # Returns + * An IdeviceFfiError on error, null on success. + * + * # Safety + * All pointers must be valid and non-null. + */ +struct IdeviceFfiError *wda_bridge_endpoints(struct WdaBridgeHandle *handle, + struct WdaBridgeEndpointsC **out_endpoints); + +/** + * Frees a WdaBridgeEndpointsC struct and its heap-allocated string fields. + * + * # Safety + * `endpoints` must be a pointer returned by `wda_bridge_endpoints` or NULL. + */ +void wda_bridge_endpoints_free(struct WdaBridgeEndpointsC *endpoints); + +/** + * Frees a WDA bridge handle. Dropping aborts the underlying forwarder tasks. + * + * # Safety + * `handle` must be a pointer returned by this library or NULL. + */ +void wda_bridge_free(struct WdaBridgeHandle *handle); + #endif /* IDEVICE_H */ diff --git a/idevice/libidevice_ffi.a b/idevice/libidevice_ffi.a index 58f0b92..3fb7e2d 100644 Binary files a/idevice/libidevice_ffi.a and b/idevice/libidevice_ffi.a differ