diff --git a/StikJIT/JSSupport/IDeviceJSBridgeDebugProxy.m b/StikJIT/JSSupport/IDeviceJSBridgeDebugProxy.m new file mode 100644 index 00000000..23ef5c7d --- /dev/null +++ b/StikJIT/JSSupport/IDeviceJSBridgeDebugProxy.m @@ -0,0 +1,33 @@ +// +// IDeviceJSBridgeDebugProxy.m +// StikJIT +// +// Created by s s on 2025/4/25. +// +@import Foundation; +@import JavaScriptCore; +#import "JSSupport.h" +#import "../idevice/JITEnableContext.h" +#import "../idevice/idevice.h" +#include "../idevice/jit.h" + +NSString* handleJSContextSendDebugCommand(JSContext* context, NSString* commandStr, DebugProxyHandle* debugProxy) { + DebugserverCommandHandle* command = 0; + + command = debugserver_command_new([commandStr UTF8String], NULL, 0); + + char* attach_response = 0; + IdeviceFfiError* err = debug_proxy_send_command2(debugProxy, command, &attach_response); + debugserver_command_free(command); + if (err) { + context.exception = [JSValue valueWithObject:[NSString stringWithFormat:@"error code %d, msg %s", err->code, err->message] inContext:context]; + idevice_error_free(err); + return nil; + } + NSString* commandResponse = nil; + if(attach_response) { + commandResponse = @(attach_response); + } + idevice_string_free(attach_response); + return commandResponse; +} diff --git a/StikJIT/JSSupport/JSSupport.h b/StikJIT/JSSupport/JSSupport.h new file mode 100644 index 00000000..bd4f51e2 --- /dev/null +++ b/StikJIT/JSSupport/JSSupport.h @@ -0,0 +1,11 @@ +// +// JSSupport.h +// StikJIT +// +// Created by s s on 2025/4/24. +// +@import WebKit; +@import JavaScriptCore; +#include "../idevice/jit.h" + +NSString* handleJSContextSendDebugCommand(JSContext* context, NSString* commandStr, DebugProxyHandle* debugProxy); diff --git a/StikJIT/JSSupport/RunJSView.swift b/StikJIT/JSSupport/RunJSView.swift new file mode 100644 index 00000000..420cb041 --- /dev/null +++ b/StikJIT/JSSupport/RunJSView.swift @@ -0,0 +1,81 @@ +// +// RunJSView.swift +// StikJIT +// +// Created by s s on 2025/4/24. +// + +import SwiftUI +import JavaScriptCore + + +class RunJSViewModel : ObservableObject { + var context: JSContext? + @Published var logs: [String] = [] + @Published var scriptName : String = "Script" + var pid: Int + var debugProxy: OpaquePointer? + var semaphore: dispatch_semaphore_t? + + init(pid: Int, debugProxy: OpaquePointer?, semaphore: dispatch_semaphore_t?) { + self.pid = pid + self.debugProxy = debugProxy + self.semaphore = semaphore + } + + func runScript(path: URL) throws { + let scriptContent = try String(contentsOf: path, encoding: .utf8) + scriptName = path.lastPathComponent + + let getPidFunction: @convention(block) () -> Int = { + return self.pid + } + + let sendCommandFunction: @convention(block) (String?) -> String? = { commandStr in + guard let commandStr else { + self.context?.exception = JSValue(object: "command should not be nil", in: self.context!) + return nil + } + + return handleJSContextSendDebugCommand(self.context, commandStr, self.debugProxy) ?? "" + } + + let logFunction: @convention(block) (String) -> Void = { logStr in + DispatchQueue.main.async { + self.logs.append(logStr) + } + } + + context = JSContext() + context?.setObject(getPidFunction, forKeyedSubscript: "get_pid" as NSString) + context?.setObject(sendCommandFunction, forKeyedSubscript: "send_command" as NSString) + context?.setObject(logFunction, forKeyedSubscript: "log" as NSString) + + context?.evaluateScript(scriptContent) + if let semaphore { + semaphore.signal() + } + DispatchQueue.main.async { + if let exception = self.context?.exception { + self.logs.append(exception.debugDescription) + } + self.logs.append("Script Execution Completed.") + } + } + +} + +struct RunJSView : View { + @ObservedObject var model : RunJSViewModel + @State var webViewShow = false + + var body: some View { + List { + ForEach(model.logs, id: \.self) { logStr in + Text(logStr) + } + } + .navigationTitle("Running \(model.scriptName)") + } + +} diff --git a/StikJIT/JSSupport/ScriptEditorView.swift b/StikJIT/JSSupport/ScriptEditorView.swift new file mode 100644 index 00000000..5c98b649 --- /dev/null +++ b/StikJIT/JSSupport/ScriptEditorView.swift @@ -0,0 +1,50 @@ +// +// ScriptEditorView.swift +// StikDebug +// +// Created by s s on 2025/7/4. +// + +import SwiftUI + +struct ScriptEditorView: View { + let scriptURL: URL + @State private var scriptContent: String = "" + @Environment(\.dismiss) private var dismiss + + var body: some View { + VStack { + TextEditor(text: $scriptContent) + .padding() + .border(Color.gray, width: 1) + .navigationTitle(scriptURL.lastPathComponent) + .navigationBarTitleDisplayMode(.inline) + .font(.system(.footnote, design: .monospaced)) + + HStack { + Button("Cancel") { + dismiss() + } + .buttonStyle(.bordered) + + Spacer() + + Button("Save") { + saveScript() + dismiss() + } + .buttonStyle(.borderedProminent) + } + .padding() + } + .onAppear(perform: loadScript) + } + + private func loadScript() { + scriptContent = (try? String(contentsOf: scriptURL)) ?? "" + } + + private func saveScript() { + try? scriptContent.write(to: scriptURL, atomically: true, encoding: .utf8) + } +} diff --git a/StikJIT/JSSupport/ScriptListView.swift b/StikJIT/JSSupport/ScriptListView.swift new file mode 100644 index 00000000..8d6b4c94 --- /dev/null +++ b/StikJIT/JSSupport/ScriptListView.swift @@ -0,0 +1,139 @@ +// +// ScriptListView.swift +// StikDebug +// +// Created by s s on 2025/7/4. +// + +import SwiftUI + +struct ScriptListView: View { + @State private var scripts: [URL] = [] + @State private var selectedScript: URL? + @State private var navigateToEditor = false + @State private var showNewFileAlert = false + @State private var newFileName = "" + @AppStorage("DefaultScriptName") var defaultScriptName = "attachDetach.js" + + var body: some View { + NavigationStack { + List { + Section { + ForEach(scripts, id: \.self) { script in + NavigationLink { + ScriptEditorView(scriptURL: script) + } label: { + HStack { + Text(script.lastPathComponent) + .font(.headline) + + if(defaultScriptName == script.lastPathComponent) { + Spacer() + Image(systemName: "star.fill") + } + } + } + .swipeActions(edge: .trailing) { + Button(role: .destructive) { + deleteScript(script) + } label: { + Label("Delete", systemImage: "trash") + } + + Button { + saveDefaultScript(script) + } label: { + Label("Set Default", systemImage: "star") + } + .tint(.blue) + } + } + } footer: { + Text("Swipe left to set a script as the default script. Enable script execution after connecting in settings.") + } + } + .navigationTitle("JavaScript Files") + .toolbar { + ToolbarItem(placement: .primaryAction) { + Button(action: { + showNewFileAlert = true + }) { + Label("New Script", systemImage: "plus") + } + } + } + .onAppear(perform: loadScripts) + .alert("New Script", isPresented: $showNewFileAlert, actions: { + TextField("Filename", text: $newFileName) + Button("Create", action: createNewScript) + Button("Cancel", role: .cancel) {} + }) + + } + } + + private func scriptsDirectory() -> URL { + let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + .appendingPathComponent("scripts") + + var isDir : ObjCBool = false + var isDirExist = FileManager.default.fileExists(atPath: dir.path(), isDirectory: &isDir) + + do { + if isDirExist && !isDir.boolValue { + try FileManager.default.removeItem(at: dir) + isDirExist = false + } + + if !isDirExist { + try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + try FileManager.default.copyItem(at: Bundle.main.url(forResource: "attachDetach", withExtension: "js")!, to: dir.appendingPathComponent("attachDetach.js")) + } + } catch { + showAlert(title: "Unable to Create Scripts Folder", message: error.localizedDescription, showOk: true) + } + return dir + } + + private func loadScripts() { + let dir = scriptsDirectory() + scripts = (try? FileManager.default.contentsOfDirectory(at: dir, includingPropertiesForKeys: nil))? + .filter { $0.pathExtension == "js" } ?? [] + + } + + private func saveDefaultScript(_ url: URL) { + defaultScriptName = url.lastPathComponent + } + + private func createNewScript() { + guard !newFileName.isEmpty else { return } + var filename = newFileName + if !filename.hasSuffix(".js") { + filename += ".js" + } + + let newURL = scriptsDirectory().appendingPathComponent(filename) + + guard !FileManager.default.fileExists(atPath: newURL.path) else { + showAlert(title: "Failed to Create New Script", message: "A script with the same name already exists.", showOk: true) + return + } + + do { + try "".write(to: newURL, atomically: true, encoding: .utf8) + newFileName = "" + loadScripts() + } catch { + print("Error creating file: \(error)") + } + } + + private func deleteScript(_ url: URL) { + try? FileManager.default.removeItem(at: url) + if url.lastPathComponent == defaultScriptName { + UserDefaults.standard.removeObject(forKey: "DefaultScriptName") + } + loadScripts() + } +} diff --git a/StikJIT/JSSupport/attachDetach.js b/StikJIT/JSSupport/attachDetach.js new file mode 100644 index 00000000..92dde880 --- /dev/null +++ b/StikJIT/JSSupport/attachDetach.js @@ -0,0 +1,18 @@ +function attach() { + log("Welcome to use StikDebug's srcipting feature! Here you can use JavaScript to customize your debug experience!") + log("This script is a demo script that attaches to the connected app and detaches from it.") + + // get pid of the connected app + let pid = get_pid(); + log(`pid = ${pid}`) + + // attach + let attachResponse = send_command(`vAttach;${pid.toString(16)}`) + log(`attach_response = ${attachResponse}`) + + // detach + let detachResponse = send_command(`D`) + log(`detachResponse = ${detachResponse}`) +} + +attach() diff --git a/StikJIT/StikJIT-Bridging-Header.h b/StikJIT/StikJIT-Bridging-Header.h index 963bed29..c4ea98d7 100644 --- a/StikJIT/StikJIT-Bridging-Header.h +++ b/StikJIT/StikJIT-Bridging-Header.h @@ -5,3 +5,4 @@ #include "idevice/JITEnableContext.h" #include "idevice/idevice.h" #include "idevice/heartbeat.h" +#include "JSSupport/JSSupport.h" diff --git a/StikJIT/StikJITApp.swift b/StikJIT/StikJITApp.swift index 1d5bbd98..e41a0bac 100644 --- a/StikJIT/StikJITApp.swift +++ b/StikJIT/StikJITApp.swift @@ -783,9 +783,9 @@ func isPairing() -> Bool { let pairingpath = URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path var pairingFile: IdevicePairingFile? let err = idevice_pairing_file_read(pairingpath, &pairingFile) - if err != IdeviceSuccess { - print("Failed to read pairing file: \(err)") - if err.rawValue == -9 { // InvalidHostID is -9 + if let err { + print("Failed to read pairing file: \(err.pointee.code)") + if err.pointee.code == -9 { // InvalidHostID is -9 return false } return false @@ -923,7 +923,7 @@ struct LoadingView: View { } } -public func showAlert(title: String, message: String, showOk: Bool, showTryAgain: Bool = false, primaryButtonText: String? = nil, messageType: MessageType = .error, completion: @escaping (Bool) -> Void) { +public func showAlert(title: String, message: String, showOk: Bool, showTryAgain: Bool = false, primaryButtonText: String? = nil, messageType: MessageType = .error, completion: ((Bool) -> Void)? = nil) { DispatchQueue.main.async { guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return @@ -935,12 +935,12 @@ public func showAlert(title: String, message: String, showOk: Bool, showTryAgain message: message, onDismiss: { rootViewController?.presentedViewController?.dismiss(animated: true) - completion(false) + completion?(false) }, showButton: true, primaryButtonText: primaryButtonText ?? "Try Again", onPrimaryButtonTap: { - completion(true) + completion?(true) }, messageType: messageType ) @@ -955,13 +955,13 @@ public func showAlert(title: String, message: String, showOk: Bool, showTryAgain message: message, onDismiss: { rootViewController?.presentedViewController?.dismiss(animated: true) - completion(true) + completion?(true) }, showButton: true, primaryButtonText: primaryButtonText ?? "OK", onPrimaryButtonTap: { rootViewController?.presentedViewController?.dismiss(animated: true) - completion(true) + completion?(true) }, messageType: messageType ) @@ -976,7 +976,7 @@ public func showAlert(title: String, message: String, showOk: Bool, showTryAgain message: message, onDismiss: { rootViewController?.presentedViewController?.dismiss(animated: true) - completion(false) + completion?(false) }, showButton: false, messageType: messageType diff --git a/StikJIT/Utilities/mountDDI.swift b/StikJIT/Utilities/mountDDI.swift index 3cd06f9e..7cf9d342 100644 --- a/StikJIT/Utilities/mountDDI.swift +++ b/StikJIT/Utilities/mountDDI.swift @@ -63,7 +63,7 @@ func isMounted() -> Bool { // Read pairing file var pairingFile: IdevicePairingFile? let err = idevice_pairing_file_read(pairingFilePath, &pairingFile) - if err != IdeviceSuccess { + if let err { print("Failed to read pairing file: \(err)") return false } @@ -71,26 +71,26 @@ func isMounted() -> Bool { // Create TCP provider var provider: TcpProviderHandle? let providerError = idevice_tcp_provider_new(sockaddrPointer, pairingFile, "ImageMounterTest", &provider) - if providerError != IdeviceSuccess { + if let providerError { print("Failed to create TCP provider: \(providerError)") return false } // Connect to image mounter var client: ImageMounterHandle? - let connectError = image_mounter_connect_tcp(provider, &client) - if connectError != IdeviceSuccess { + let connectError = image_mounter_connect(provider, &client) + if let connectError { print("Failed to connect to image mounter: \(connectError)") return false } - tcp_provider_free(provider) + idevice_provider_free(provider) print("wow") var devices: UnsafeMutableRawPointer? var devicesLen: size_t = 0 let listError = image_mounter_copy_devices(client, &devices, &devicesLen) - if listError == IdeviceSuccess { + if listError == nil { let deviceList = devices?.assumingMemoryBound(to: plist_t.self) var devices: [String] = [] for i in 0.. DebugAppCallback? { + let selectedScriptURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + .appendingPathComponent("scripts").appendingPathComponent(selectedScript) + + if !FileManager.default.fileExists(atPath: selectedScriptURL.path()) { + return nil + } + + return { pid, debugProxyHandle, semaphore in + jsModel = RunJSViewModel(pid: Int(pid), debugProxy: debugProxyHandle, semaphore: semaphore) + scriptViewShow = true + DispatchQueue.global(qos: .background).async { + do { + try jsModel?.runScript(path: selectedScriptURL) + } catch { + showAlert(title: "Error Occurred While Executing the Default Script.", message: error.localizedDescription, showOk: true) + } + } + } + } + private func startJITInBackground(with bundleID: String) { isProcessing = true @@ -380,22 +429,17 @@ struct HomeView: View { LogManager.shared.addInfoLog("Starting Debug for \(bundleID)") DispatchQueue.global(qos: .background).async { - let success = JITEnableContext.shared.debugApp(withBundleID: bundleID, logger: { message in if let message = message { // Log messages from the JIT process LogManager.shared.addInfoLog(message) } - }) + }, jsCallback: useDefaultScript ? getJsCallback() : nil) DispatchQueue.main.async { LogManager.shared.addInfoLog("Debug process completed for \(bundleID)") isProcessing = false - - if success && doAutoQuitAfterEnablingJIT { - exit(0) - } } } } @@ -409,21 +453,17 @@ struct HomeView: View { DispatchQueue.global(qos: .background).async { let success = JITEnableContext.shared.debugApp(withPID: Int32(pid), logger: { message in - + if let message = message { // Log messages from the JIT process LogManager.shared.addInfoLog(message) } - }) + }, jsCallback: useDefaultScript ? getJsCallback() : nil) DispatchQueue.main.async { LogManager.shared.addInfoLog("JIT process completed for \(pid)") - showAlert(title: "Success", message: "JIT has been enabled for pid \(pid).", showOk: true, messageType: .success, completion: { _ in }) + showAlert(title: "Success", message: "JIT has been enabled for pid \(pid).", showOk: true, messageType: .success) isProcessing = false - - if success && doAutoQuitAfterEnablingJIT { - exit(0) - } } } } diff --git a/StikJIT/Views/MainTabView.swift b/StikJIT/Views/MainTabView.swift index 57b98f45..84cf4680 100644 --- a/StikJIT/Views/MainTabView.swift +++ b/StikJIT/Views/MainTabView.swift @@ -24,6 +24,10 @@ struct MainTabView: View { .tabItem { Label("Home", systemImage: "house") } + ScriptListView() + .tabItem { + Label("Scripts", systemImage: "scroll") + } SettingsView() .tabItem { Label("Settings", systemImage: "gearshape.fill") diff --git a/StikJIT/Views/SettingsView.swift b/StikJIT/Views/SettingsView.swift index 518ee678..a78df80a 100644 --- a/StikJIT/Views/SettingsView.swift +++ b/StikJIT/Views/SettingsView.swift @@ -11,7 +11,7 @@ struct SettingsView: View { @AppStorage("username") private var username = "User" @AppStorage("customAccentColor") private var customAccentColorHex: String = "" @AppStorage("selectedAppIcon") private var selectedAppIcon: String = "AppIcon" - @AppStorage("autoQuitAfterEnablingJIT") private var doAutoQuitAfterEnablingJIT = false + @AppStorage("useDefaultScript") private var useDefaultScript = false @State private var isShowingPairingFilePicker = false @Environment(\.colorScheme) private var colorScheme @@ -271,7 +271,7 @@ struct SettingsView: View { .foregroundColor(.primary) .padding(.bottom, 4) - Toggle("Automatically Quit After Enabling JIT", isOn: $doAutoQuitAfterEnablingJIT) + Toggle("Run Default Script After Connecting", isOn: $useDefaultScript) .foregroundColor(.primary) .padding(.vertical, 6) } diff --git a/StikJIT/idevice/JITEnableContext.h b/StikJIT/idevice/JITEnableContext.h index 28d47246..c85f0753 100644 --- a/StikJIT/idevice/JITEnableContext.h +++ b/StikJIT/idevice/JITEnableContext.h @@ -7,6 +7,7 @@ @import Foundation; @import UIKit; #include "idevice.h" +#include "jit.h" typedef void (^HeartbeatCompletionHandler)(int result, NSString *message); typedef void (^LogFuncC)(const char* message, ...); @@ -16,8 +17,8 @@ typedef void (^LogFunc)(NSString *message); @property (class, readonly)JITEnableContext* shared; - (IdevicePairingFile*)getPairingFileWithError:(NSError**)error; - (void)startHeartbeatWithCompletionHandler:(HeartbeatCompletionHandler)completionHandler logger:(LogFunc)logger; -- (BOOL)debugAppWithBundleID:(NSString*)bundleID logger:(LogFunc)logger; -- (BOOL)debugAppWithPID:(int)pid logger:(LogFunc)logger; +- (BOOL)debugAppWithBundleID:(NSString*)bundleID logger:(LogFunc)logger jsCallback:(DebugAppCallback)jsCallback; +- (BOOL)debugAppWithPID:(int)pid logger:(LogFunc)logger jsCallback:(DebugAppCallback)jsCallback; - (NSDictionary*)getAppListWithError:(NSError**)error; - (UIImage*)getAppIconWithBundleId:(NSString*)bundleId error:(NSError**)error; @end diff --git a/StikJIT/idevice/JITEnableContext.m b/StikJIT/idevice/JITEnableContext.m index e7c4631e..4f4af946 100644 --- a/StikJIT/idevice/JITEnableContext.m +++ b/StikJIT/idevice/JITEnableContext.m @@ -18,8 +18,8 @@ JITEnableContext* sharedJITContext = nil; @implementation JITEnableContext { - int heartbeatSessionId; - TcpProviderHandle* provider; + bool heartbeatRunning; + IdeviceProviderHandle* provider; } + (instancetype)shared { @@ -33,7 +33,7 @@ - (instancetype)init { NSFileManager* fm = [NSFileManager defaultManager]; NSURL* docPathUrl = [fm URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject; NSURL* logURL = [docPathUrl URLByAppendingPathComponent:@"idevice_log.txt"]; - idevice_init_logger(Debug, Debug, (char*)logURL.path.UTF8String); + idevice_init_logger(Info, Debug, (char*)logURL.path.UTF8String); return self; } @@ -80,9 +80,9 @@ - (IdevicePairingFile*)getPairingFileWithError:(NSError**)error { } IdevicePairingFile* pairingFile = NULL; - IdeviceErrorCode err = idevice_pairing_file_read(pairingFileURL.fileSystemRepresentation, &pairingFile); - if (err != IdeviceSuccess) { - *error = [self errorWithStr:@"Failed to read pairing file!" code:err]; + IdeviceFfiError* err = idevice_pairing_file_read(pairingFileURL.fileSystemRepresentation, &pairingFile); + if (err) { + *error = [self errorWithStr:@"Failed to read pairing file!" code:err->code]; return nil; } return pairingFile; @@ -106,11 +106,13 @@ - (void)startHeartbeatWithCompletionHandler:(HeartbeatCompletionHandler)completi return; } - self->heartbeatSessionId = arc4random(); + if(heartbeatRunning) { + return; + } startHeartbeat( pairingFile, &provider, - &heartbeatSessionId, + &heartbeatRunning, ^(int result, const char *message) { completionHandler(result, [NSString stringWithCString:message @@ -129,7 +131,7 @@ - (void)ensureHeartbeat { } } -- (BOOL)debugAppWithBundleID:(NSString*)bundleID logger:(LogFunc)logger { +- (BOOL)debugAppWithBundleID:(NSString*)bundleID logger:(LogFunc)logger jsCallback:(DebugAppCallback)jsCallback { if (!provider) { if (logger) { logger(@"Provider not initialized!"); @@ -142,10 +144,10 @@ - (BOOL)debugAppWithBundleID:(NSString*)bundleID logger:(LogFunc)logger { return debug_app(provider, [bundleID UTF8String], - [self createCLogger:logger]) == 0; + [self createCLogger:logger], jsCallback) == 0; } -- (BOOL)debugAppWithPID:(int)pid logger:(LogFunc)logger { +- (BOOL)debugAppWithPID:(int)pid logger:(LogFunc)logger jsCallback:(DebugAppCallback)jsCallback { if (!provider) { if (logger) { logger(@"Provider not initialized!"); @@ -158,7 +160,7 @@ - (BOOL)debugAppWithPID:(int)pid logger:(LogFunc)logger { return debug_app_pid(provider, pid, - [self createCLogger:logger]) == 0; + [self createCLogger:logger], jsCallback) == 0; } - (NSDictionary*)getAppListWithError:(NSError**)error { @@ -194,9 +196,8 @@ - (UIImage*)getAppIconWithBundleId:(NSString*)bundleId error:(NSError**)error { } - (void)dealloc { - heartbeatSessionId = arc4random(); if (provider) { - tcp_provider_free(provider); + idevice_provider_free(provider); } } diff --git a/StikJIT/idevice/applist.h b/StikJIT/idevice/applist.h index 153b1d38..1696ddb5 100644 --- a/StikJIT/idevice/applist.h +++ b/StikJIT/idevice/applist.h @@ -10,7 +10,7 @@ @import Foundation; @import UIKit; -NSDictionary* list_installed_apps(TcpProviderHandle* provider, NSString** error); -UIImage* getAppIcon(TcpProviderHandle* provider, NSString* bundleID, NSString** error); +NSDictionary* list_installed_apps(IdeviceProviderHandle* provider, NSString** error); +UIImage* getAppIcon(IdeviceProviderHandle* provider, NSString* bundleID, NSString** error); #endif /* APPLIST_H */ diff --git a/StikJIT/idevice/applist.m b/StikJIT/idevice/applist.m index 06d49a29..95478ffe 100644 --- a/StikJIT/idevice/applist.m +++ b/StikJIT/idevice/applist.m @@ -11,16 +11,16 @@ #include #import "applist.h" -NSDictionary* list_installed_apps(TcpProviderHandle* provider, NSString** error) { +NSDictionary* list_installed_apps(IdeviceProviderHandle* provider, NSString** error) { InstallationProxyClientHandle *client = NULL; - if (installation_proxy_connect_tcp(provider, &client) != IdeviceSuccess) { + if (installation_proxy_connect_tcp(provider, &client)) { *error = @"Failed to connect to installation proxy"; return nil; } void *apps = NULL; size_t count = 0; - if (installation_proxy_get_apps(client, "User", NULL, 0, &apps, &count) != IdeviceSuccess) { + if (installation_proxy_get_apps(client, "User", NULL, 0, &apps, &count)) { installation_proxy_client_free(client); *error = @"Failed to get apps"; return nil; @@ -70,16 +70,16 @@ return result; } -UIImage* getAppIcon(TcpProviderHandle* provider, NSString* bundleID, NSString** error) { +UIImage* getAppIcon(IdeviceProviderHandle* provider, NSString* bundleID, NSString** error) { SpringBoardServicesClientHandle *client = NULL; - if (springboard_services_connect_tcp(provider, &client) != IdeviceSuccess) { + if (springboard_services_connect(provider, &client)) { *error = @"Failed to connect to SpringBoard Services"; return nil; } void *pngData = NULL; size_t dataLen = 0; - if (springboard_services_get_icon(client, [bundleID UTF8String], &pngData, &dataLen) != IdeviceSuccess) { + if (springboard_services_get_icon(client, [bundleID UTF8String], &pngData, &dataLen)) { springboard_services_free(client); *error = @"Failed to get app icon"; return nil; diff --git a/StikJIT/idevice/heartbeat.h b/StikJIT/idevice/heartbeat.h index 5e8f5f63..26233800 100644 --- a/StikJIT/idevice/heartbeat.h +++ b/StikJIT/idevice/heartbeat.h @@ -17,6 +17,6 @@ typedef void (^LogFuncC)(const char* message, ...); extern bool isHeartbeat; extern NSDate* lastHeartbeatDate; -void startHeartbeat(IdevicePairingFile* pairintFile, TcpProviderHandle** provider, int* heartbeatSessionId, HeartbeatCompletionHandlerC completion, LogFuncC logger); +void startHeartbeat(IdevicePairingFile* pairintFile, IdeviceProviderHandle** provider, bool* isHeartbeat, HeartbeatCompletionHandlerC completion, LogFuncC logger); #endif /* HEARTBEAT_H */ diff --git a/StikJIT/idevice/heartbeat.m b/StikJIT/idevice/heartbeat.m index f83c286d..8720648a 100644 --- a/StikJIT/idevice/heartbeat.m +++ b/StikJIT/idevice/heartbeat.m @@ -15,74 +15,67 @@ bool isHeartbeat = false; NSDate* lastHeartbeatDate = nil; -void startHeartbeat(IdevicePairingFile* pairing_file, TcpProviderHandle** provider, int* heartbeatSessionId, HeartbeatCompletionHandlerC completion, LogFuncC logger) { - int currentSessionId = *heartbeatSessionId; +void startHeartbeat(IdevicePairingFile* pairing_file, IdeviceProviderHandle** provider, bool* isHeartbeat, HeartbeatCompletionHandlerC completion, LogFuncC logger) { - isHeartbeat = true; + *isHeartbeat = true; + // Initialize logger + idevice_init_logger(Debug, Disabled, NULL); + + // Create the socket address (replace with your device's IP) struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; - if (inet_pton(AF_INET, "10.7.0.1", &addr.sin_addr) <= 0) { - logger("DEBUG: Error converting IP address."); - isHeartbeat = false; - return; - } - logger("DEBUG: Socket address created for IP 10.7.0.1"); - - IdeviceErrorCode err = IdeviceSuccess; + addr.sin_port = htons(LOCKDOWN_PORT); + inet_pton(AF_INET, "10.7.0.2", &addr.sin_addr); - logger("DEBUG: Creating TCP provider..."); - err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file, - "ExampleProvider", provider); - if (err != IdeviceSuccess) { - logger("DEBUG: Failed to create TCP provider: %d", err); - completion(err, "Failed to create TCP provider"); - isHeartbeat = false; + IdeviceFfiError* err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file, + "ExampleProvider", provider); + if (err != NULL) { + fprintf(stderr, "Failed to create TCP provider: [%d] %s", err->code, + err->message); + idevice_pairing_file_free(pairing_file); + idevice_error_free(err); + *isHeartbeat = false; return; } - logger("DEBUG: TCP provider created successfully."); - logger("DEBUG: Connecting to heartbeat..."); + // Connect to installation proxy HeartbeatClientHandle *client = NULL; - err = heartbeat_connect_tcp(*provider, &client); - if (err != IdeviceSuccess) { - completion(err, "Failed to connect to Heartbeat"); - logger("DEBUG: Failed to connect to installation proxy: %d", err); - isHeartbeat = false; + err = heartbeat_connect(*provider, &client); + if (err != NULL) { + fprintf(stderr, "Failed to connect to installation proxy: [%d] %s", + err->code, err->message); + idevice_provider_free(*provider); + idevice_error_free(err); + *isHeartbeat = false; return; } - logger("DEBUG: Connected to heartbeat successfully."); + completion(0, "Heartbeat Completed"); u_int64_t current_interval = 15; while (1) { - if(*heartbeatSessionId != currentSessionId) { - break; - } - + // Get the new interval u_int64_t new_interval = 0; - logger("DEBUG: Sending heartbeat with current interval: %llu seconds...", current_interval); err = heartbeat_get_marco(client, current_interval, &new_interval); - if (err != IdeviceSuccess) { - logger("DEBUG: Failed to get marco: %d", err); - isHeartbeat = false; + if (err != NULL) { + fprintf(stderr, "Failed to get marco: [%d] %s", err->code, err->message); heartbeat_client_free(client); + idevice_error_free(err); + *isHeartbeat = false; return; } - logger("DEBUG: Received new interval: %llu seconds.", new_interval); current_interval = new_interval + 5; - logger("DEBUG: Sending polo reply..."); + // Reply err = heartbeat_send_polo(client); - if (err != IdeviceSuccess) { - logger("DEBUG: Failed to send polo: %d", err); - isHeartbeat = false; + if (err != NULL) { + fprintf(stderr, "Failed to get marco: [%d] %s", err->code, err->message); heartbeat_client_free(client); + idevice_error_free(err); + *isHeartbeat = false; return; } - logger("DEBUG: Polo reply sent successfully."); - lastHeartbeatDate = [NSDate date]; - isHeartbeat = true; } } diff --git a/StikJIT/idevice/idevice.h b/StikJIT/idevice/idevice.h index 6dab1c6f..b0bdec17 100644 --- a/StikJIT/idevice/idevice.h +++ b/StikJIT/idevice/idevice.h @@ -14,52 +14,22 @@ #define LOCKDOWN_PORT 62078 +typedef enum AfcFopenMode { + AfcRdOnly = 1, + AfcRw = 2, + AfcWrOnly = 3, + AfcWr = 4, + AfcAppend = 5, + AfcRdAppend = 6, +} AfcFopenMode; -typedef enum IdeviceErrorCode { - IdeviceSuccess = 0, - Socket = -1, - Ssl = -2, - SslSetup = -3, - Plist = -4, - Utf8 = -5, - UnexpectedResponse = -6, - GetProhibited = -7, - SessionInactive = -8, - InvalidHostID = -9, - NoEstablishedConnection = -10, - HeartbeatSleepyTime = -11, - HeartbeatTimeout = -12, - NotFound = -13, - CdtunnelPacketTooShort = -14, - CdtunnelPacketInvalidMagic = -15, - PacketSizeMismatch = -16, - Json = -17, - DeviceNotFound = -18, - DeviceLocked = -19, - UsbConnectionRefused = -20, - UsbBadCommand = -21, - UsbBadDevice = -22, - UsbBadVersion = -23, - BadBuildManifest = -24, - ImageNotMounted = -25, - Reqwest = -26, - InternalError = -27, - Xpc = -28, - NsKeyedArchiveError = -29, - UnknownAuxValueType = -30, - UnknownChannel = -31, - AddrParseError = -32, - DisableMemoryLimitFailed = -33, - NotEnoughBytes = -34, - Utf8Error = -35, - InvalidArgument = -36, - UnknownErrorType = -37, - AdapterIOFailed = -996, - ServiceNotFound = -997, - BufferTooSmall = -998, - InvalidString = -999, - InvalidArg = -1000, -} IdeviceErrorCode; +/** + * Link type for creating hard or symbolic links + */ +typedef enum AfcLinkType { + Hard = 1, + Symbolic = 2, +} AfcLinkType; typedef enum IdeviceLogLevel { Disabled = 0, @@ -79,12 +49,23 @@ typedef enum IdeviceLoggerError { typedef struct AdapterHandle AdapterHandle; +typedef struct AdapterStreamHandle AdapterStreamHandle; + +typedef struct AfcClientHandle AfcClientHandle; + +/** + * Handle for an open file on the device + */ +typedef struct AfcFileHandle AfcFileHandle; + +typedef struct AmfiClientHandle AmfiClientHandle; + typedef struct CoreDeviceProxyHandle CoreDeviceProxyHandle; /** * Opaque handle to a DebugProxyClient */ -typedef struct DebugProxyAdapterHandle DebugProxyAdapterHandle; +typedef struct DebugProxyHandle DebugProxyHandle; typedef struct HeartbeatClientHandle HeartbeatClientHandle; @@ -98,40 +79,83 @@ typedef struct IdeviceHandle IdeviceHandle; */ typedef struct IdevicePairingFile IdevicePairingFile; +typedef struct IdeviceProviderHandle IdeviceProviderHandle; + typedef struct IdeviceSocketHandle IdeviceSocketHandle; typedef struct ImageMounterHandle ImageMounterHandle; typedef struct InstallationProxyClientHandle InstallationProxyClientHandle; +/** + * Opaque handle to a ProcessControlClient + */ +typedef struct LocationSimulationHandle LocationSimulationHandle; + typedef struct LockdowndClientHandle LockdowndClientHandle; +typedef struct MisagentClientHandle MisagentClientHandle; + +typedef struct OsTraceRelayClientHandle OsTraceRelayClientHandle; + +typedef struct OsTraceRelayReceiverHandle OsTraceRelayReceiverHandle; + /** * Opaque handle to a ProcessControlClient */ -typedef struct ProcessControlAdapterHandle ProcessControlAdapterHandle; +typedef struct ProcessControlHandle ProcessControlHandle; + +typedef struct ReadWriteOpaque ReadWriteOpaque; /** * Opaque handle to a RemoteServerClient */ -typedef struct RemoteServerAdapterHandle RemoteServerAdapterHandle; +typedef struct RemoteServerHandle RemoteServerHandle; + +/** + * Opaque handle to an RsdHandshake + */ +typedef struct RsdHandshakeHandle RsdHandshakeHandle; typedef struct SpringBoardServicesClientHandle SpringBoardServicesClientHandle; -typedef struct TcpProviderHandle TcpProviderHandle; +typedef struct SyslogRelayClientHandle SyslogRelayClientHandle; typedef struct UsbmuxdAddrHandle UsbmuxdAddrHandle; typedef struct UsbmuxdConnectionHandle UsbmuxdConnectionHandle; -typedef struct UsbmuxdProviderHandle UsbmuxdProviderHandle; +typedef struct Vec_u64 Vec_u64; + +typedef struct sockaddr sockaddr; + +typedef struct IdeviceFfiError { + int32_t code; + const char *message; +} IdeviceFfiError; /** - * Opaque handle to an XPCDevice + * File information structure for C bindings */ -typedef struct XPCDeviceAdapterHandle XPCDeviceAdapterHandle; +typedef struct AfcFileInfo { + size_t size; + size_t blocks; + int64_t creation; + int64_t modified; + char *st_nlink; + char *st_ifmt; + char *st_link_target; +} AfcFileInfo; -typedef struct sockaddr sockaddr; +/** + * Device information structure for C bindings + */ +typedef struct AfcDeviceInfo { + char *model; + size_t total_bytes; + size_t free_bytes; + size_t block_size; +} AfcDeviceInfo; /** * Represents a debugserver command @@ -142,17 +166,68 @@ typedef struct DebugserverCommandHandle { uintptr_t argv_count; } DebugserverCommandHandle; +typedef struct SyslogLabel { + const char *subsystem; + const char *category; +} SyslogLabel; + +typedef struct OsTraceLog { + uint32_t pid; + int64_t timestamp; + uint8_t level; + const char *image_name; + const char *filename; + const char *message; + const struct SyslogLabel *label; +} OsTraceLog; + /** - * Opaque handle to an XPCService + * C-compatible representation of an RSD service */ -typedef struct XPCServiceHandle { +typedef struct CRsdService { + /** + * Service name (null-terminated string) + */ + char *name; + /** + * Required entitlement (null-terminated string) + */ char *entitlement; + /** + * Port number + */ uint16_t port; + /** + * Whether service uses remote XPC + */ bool uses_remote_xpc; + /** + * Number of features + */ + size_t features_count; + /** + * Array of feature strings + */ char **features; - uintptr_t features_count; + /** + * Service version (-1 if not present) + */ int64_t service_version; -} XPCServiceHandle; +} CRsdService; + +/** + * Array of RSD services returned by rsd_get_services + */ +typedef struct CRsdServiceArray { + /** + * Array of services + */ + struct CRsdService *services; + /** + * Number of services in array + */ + size_t count; +} CRsdServiceArray; /** * Creates a new Idevice connection @@ -163,15 +238,15 @@ typedef struct XPCServiceHandle { * * [`idevice`] - On success, will be set to point to a newly allocated Idevice handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `label` must be a valid null-terminated C string * `idevice` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode idevice_new(struct IdeviceSocketHandle *socket, - const char *label, - struct IdeviceHandle **idevice); +struct IdeviceFfiError *idevice_new(struct IdeviceSocketHandle *socket, + const char *label, + struct IdeviceHandle **idevice); /** * Creates a new Idevice connection @@ -183,17 +258,17 @@ enum IdeviceErrorCode idevice_new(struct IdeviceSocketHandle *socket, * * [`idevice`] - On success, will be set to point to a newly allocated Idevice handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `addr` must be a valid sockaddr * `label` must be a valid null-terminated C string * `idevice` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode idevice_new_tcp_socket(const struct sockaddr *addr, - socklen_t addr_len, - const char *label, - struct IdeviceHandle **idevice); +struct IdeviceFfiError *idevice_new_tcp_socket(const struct sockaddr *addr, + socklen_t addr_len, + const char *label, + struct IdeviceHandle **idevice); /** * Gets the device type @@ -203,14 +278,14 @@ enum IdeviceErrorCode idevice_new_tcp_socket(const struct sockaddr *addr, * * [`device_type`] - On success, will be set to point to a newly allocated string containing the device type * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `idevice` must be a valid, non-null pointer to an Idevice handle * `device_type` must be a valid, non-null pointer to a location where the string pointer will be stored */ -enum IdeviceErrorCode idevice_get_type(struct IdeviceHandle *idevice, - char **device_type); +struct IdeviceFfiError *idevice_get_type(struct IdeviceHandle *idevice, + char **device_type); /** * Performs RSD checkin @@ -219,12 +294,12 @@ enum IdeviceErrorCode idevice_get_type(struct IdeviceHandle *idevice, * * [`idevice`] - The Idevice handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `idevice` must be a valid, non-null pointer to an Idevice handle */ -enum IdeviceErrorCode idevice_rsd_checkin(struct IdeviceHandle *idevice); +struct IdeviceFfiError *idevice_rsd_checkin(struct IdeviceHandle *idevice); /** * Starts a TLS session @@ -234,14 +309,14 @@ enum IdeviceErrorCode idevice_rsd_checkin(struct IdeviceHandle *idevice); * * [`pairing_file`] - The pairing file to use for TLS * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `idevice` must be a valid, non-null pointer to an Idevice handle * `pairing_file` must be a valid, non-null pointer to a pairing file handle */ -enum IdeviceErrorCode idevice_start_session(struct IdeviceHandle *idevice, - const struct IdevicePairingFile *pairing_file); +struct IdeviceFfiError *idevice_start_session(struct IdeviceHandle *idevice, + const struct IdevicePairingFile *pairing_file); /** * Frees an Idevice handle @@ -271,16 +346,21 @@ void idevice_string_free(char *string); * Connects the adapter to a specific port * * # Arguments - * * [`handle`] - The adapter handle + * * [`adapter_handle`] - The adapter handle * * [`port`] - The port to connect to + * * [`stream_handle`] - A pointer to allocate the new stream to * * # Returns - * An error code indicating success or failure + * Null on success, an IdeviceFfiError otherwise * * # Safety - * `handle` must be a valid pointer to a handle allocated by this library + * `handle` must be a valid pointer to a handle allocated by this library. + * Any stream allocated must be used in the same thread as the adapter. The handles are NOT thread + * safe. */ -enum IdeviceErrorCode adapter_connect(struct AdapterHandle *handle, uint16_t port); +struct IdeviceFfiError *adapter_connect(struct AdapterHandle *adapter_handle, + uint16_t port, + struct ReadWriteOpaque **stream_handle); /** * Enables PCAP logging for the adapter @@ -290,27 +370,27 @@ enum IdeviceErrorCode adapter_connect(struct AdapterHandle *handle, uint16_t por * * [`path`] - The path to save the PCAP file (null-terminated string) * * # Returns - * An error code indicating success or failure + * Null on success, an IdeviceFfiError otherwise * * # Safety * `handle` must be a valid pointer to a handle allocated by this library * `path` must be a valid null-terminated string */ -enum IdeviceErrorCode adapter_pcap(struct AdapterHandle *handle, const char *path); +struct IdeviceFfiError *adapter_pcap(struct AdapterHandle *handle, const char *path); /** * Closes the adapter connection * * # Arguments - * * [`handle`] - The adapter handle + * * [`handle`] - The adapter stream handle * * # Returns - * An error code indicating success or failure + * Null on success, an IdeviceFfiError otherwise * * # Safety * `handle` must be a valid pointer to a handle allocated by this library */ -enum IdeviceErrorCode adapter_close(struct AdapterHandle *handle); +struct IdeviceFfiError *adapter_close(struct AdapterStreamHandle *handle); /** * Sends data through the adapter @@ -321,15 +401,15 @@ enum IdeviceErrorCode adapter_close(struct AdapterHandle *handle); * * [`length`] - The length of the data * * # Returns - * An error code indicating success or failure + * Null on success, an IdeviceFfiError otherwise * * # Safety * `handle` must be a valid pointer to a handle allocated by this library * `data` must be a valid pointer to at least `length` bytes */ -enum IdeviceErrorCode adapter_send(struct AdapterHandle *handle, - const uint8_t *data, - uintptr_t length); +struct IdeviceFfiError *adapter_send(struct AdapterStreamHandle *handle, + const uint8_t *data, + uintptr_t length); /** * Receives data from the adapter @@ -341,51 +421,419 @@ enum IdeviceErrorCode adapter_send(struct AdapterHandle *handle, * * [`max_length`] - Maximum number of bytes that can be stored in `data` * * # Returns - * An error code indicating success or failure + * Null on success, an IdeviceFfiError otherwise * * # Safety * `handle` must be a valid pointer to a handle allocated by this library * `data` must be a valid pointer to at least `max_length` bytes * `length` must be a valid pointer to a usize */ -enum IdeviceErrorCode adapter_recv(struct AdapterHandle *handle, - uint8_t *data, - uintptr_t *length, - uintptr_t max_length); +struct IdeviceFfiError *adapter_recv(struct AdapterStreamHandle *handle, + uint8_t *data, + uintptr_t *length, + uintptr_t max_length); /** - * Automatically creates and connects to Core Device Proxy, returning a client handle + * Connects to the AFC service using a TCP provider * * # Arguments - * * [`provider`] - A TcpProvider - * * [`client`] - On success, will be set to point to a newly allocated CoreDeviceProxy handle + * * [`provider`] - An IdeviceProvider + * * [`client`] - On success, will be set to point to a newly allocated AfcClient handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `provider` must be a valid pointer to a handle allocated by this library * `client` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode core_device_proxy_connect_tcp(struct TcpProviderHandle *provider, - struct CoreDeviceProxyHandle **client); +struct IdeviceFfiError *afc_client_connect(struct IdeviceProviderHandle *provider, + struct AfcClientHandle **client); + +/** + * Creates a new AfcClient from an existing Idevice connection + * + * # Arguments + * * [`socket`] - An IdeviceSocket handle + * * [`client`] - On success, will be set to point to a newly allocated AfcClient 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 *afc_client_new(struct IdeviceHandle *socket, + struct AfcClientHandle **client); + +/** + * Frees an AfcClient handle + * + * # Arguments + * * [`handle`] - The handle to free + * + * # Safety + * `handle` must be a valid pointer to the handle that was allocated by this library, + * or NULL (in which case this function does nothing) + */ +void afc_client_free(struct AfcClientHandle *handle); + +/** + * Lists the contents of a directory on the device + * + * # Arguments + * * [`client`] - A valid AfcClient handle + * * [`path`] - Path to the directory to list (UTF-8 null-terminated) + * * [`entries`] - Will be set to point to an array of directory entries + * * [`count`] - Will be set to the number of entries + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null + * `path` must be a valid null-terminated C string + */ +struct IdeviceFfiError *afc_list_directory(struct AfcClientHandle *client, + const char *path, + char ***entries, + size_t *count); + +/** + * Creates a new directory on the device + * + * # Arguments + * * [`client`] - A valid AfcClient handle + * * [`path`] - Path of the directory to create (UTF-8 null-terminated) + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `path` must be a valid null-terminated C string + */ +struct IdeviceFfiError *afc_make_directory(struct AfcClientHandle *client, const char *path); + +/** + * Retrieves information about a file or directory + * + * # Arguments + * * [`client`] - A valid AfcClient handle + * * [`path`] - Path to the file or directory (UTF-8 null-terminated) + * * [`info`] - Will be populated with file information + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` and `path` must be valid pointers + * `info` must be a valid pointer to an AfcFileInfo struct + */ +struct IdeviceFfiError *afc_get_file_info(struct AfcClientHandle *client, + const char *path, + struct AfcFileInfo *info); + +/** + * Frees memory allocated by afc_get_file_info + * + * # Arguments + * * [`info`] - Pointer to AfcFileInfo struct to free + * + * # Safety + * `info` must be a valid pointer to an AfcFileInfo struct previously returned by afc_get_file_info + */ +void afc_file_info_free(struct AfcFileInfo *info); + +/** + * Retrieves information about the device's filesystem + * + * # Arguments + * * [`client`] - A valid AfcClient handle + * * [`info`] - Will be populated with device information + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` and `info` must be valid pointers + */ +struct IdeviceFfiError *afc_get_device_info(struct AfcClientHandle *client, + struct AfcDeviceInfo *info); + +/** + * Frees memory allocated by afc_get_device_info + * + * # Arguments + * * [`info`] - Pointer to AfcDeviceInfo struct to free + * + * # Safety + * `info` must be a valid pointer to an AfcDeviceInfo struct previously returned by afc_get_device_info + */ +void afc_device_info_free(struct AfcDeviceInfo *info); + +/** + * Removes a file or directory + * + * # Arguments + * * [`client`] - A valid AfcClient handle + * * [`path`] - Path to the file or directory to remove (UTF-8 null-terminated) + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `path` must be a valid null-terminated C string + */ +struct IdeviceFfiError *afc_remove_path(struct AfcClientHandle *client, const char *path); + +/** + * Recursively removes a directory and all its contents + * + * # Arguments + * * [`client`] - A valid AfcClient handle + * * [`path`] - Path to the directory to remove (UTF-8 null-terminated) + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `path` must be a valid null-terminated C string + */ +struct IdeviceFfiError *afc_remove_path_and_contents(struct AfcClientHandle *client, + const char *path); + +/** + * Opens a file on the device + * + * # Arguments + * * [`client`] - A valid AfcClient handle + * * [`path`] - Path to the file to open (UTF-8 null-terminated) + * * [`mode`] - File open mode + * * [`handle`] - Will be set to a new file handle on success + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null + * `path` must be a valid null-terminated C string. + * The file handle MAY NOT be used from another thread, and is + * dependant upon the client it was created by. + */ +struct IdeviceFfiError *afc_file_open(struct AfcClientHandle *client, + const char *path, + enum AfcFopenMode mode, + struct AfcFileHandle **handle); + +/** + * Closes a file handle + * + * # Arguments + * * [`handle`] - File handle to close + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *afc_file_close(struct AfcFileHandle *handle); + +/** + * Reads data from an open file + * + * # Arguments + * * [`handle`] - File handle to read from + * * [`data`] - Will be set to point to the read data + * * [`length`] - Will be set to the length of the read data + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null + */ +struct IdeviceFfiError *afc_file_read(struct AfcFileHandle *handle, uint8_t **data, size_t *length); + +/** + * Writes data to an open file + * + * # Arguments + * * [`handle`] - File handle to write to + * * [`data`] - Data to write + * * [`length`] - Length of data to write + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null + * `data` must point to at least `length` bytes + */ +struct IdeviceFfiError *afc_file_write(struct AfcFileHandle *handle, + const uint8_t *data, + size_t length); + +/** + * Creates a hard or symbolic link + * + * # Arguments + * * [`client`] - A valid AfcClient handle + * * [`target`] - Target path of the link (UTF-8 null-terminated) + * * [`source`] - Path where the link should be created (UTF-8 null-terminated) + * * [`link_type`] - Type of link to create + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null + * `target` and `source` must be valid null-terminated C strings + */ +struct IdeviceFfiError *afc_make_link(struct AfcClientHandle *client, + const char *target, + const char *source, + enum AfcLinkType link_type); + +/** + * Renames a file or directory + * + * # Arguments + * * [`client`] - A valid AfcClient handle + * * [`source`] - Current path of the file/directory (UTF-8 null-terminated) + * * [`target`] - New path for the file/directory (UTF-8 null-terminated) + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid and non-null + * `source` and `target` must be valid null-terminated C strings + */ +struct IdeviceFfiError *afc_rename_path(struct AfcClientHandle *client, + const char *source, + const char *target); + +/** + * Frees memory allocated by a file read function allocated by this library + * + * # Arguments + * * [`info`] - Pointer to AfcDeviceInfo struct to free + * + * # Safety + * `info` must be a valid pointer to an AfcDeviceInfo struct previously returned by afc_get_device_info + */ +void afc_file_read_data_free(uint8_t *data, + size_t length); + +/** + * Automatically creates and connects to AMFI service, returning a client handle + * + * # Arguments + * * [`provider`] - An IdeviceProvider + * * [`client`] - On success, will be set to point to a newly allocated AmfiClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `provider` must be a valid pointer to a handle allocated by this library + * `client` must be a valid, non-null pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *amfi_connect(struct IdeviceProviderHandle *provider, + struct AmfiClientHandle **client); + +/** + * Automatically creates and connects to AMFI service, returning a client handle + * + * # Arguments + * * [`socket`] - An IdeviceSocket handle + * * [`client`] - On success, will be set to point to a newly allocated AmfiClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `socket` must be a valid pointer to a handle allocated by this library. It is consumed, and + * should not be used again. + * `client` must be a valid, non-null pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *amfi_new(struct IdeviceHandle *socket, struct AmfiClientHandle **client); + +/** + * Shows the option in the settings UI + * + * # Arguments + * * `client` - A valid AmfiClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *amfi_reveal_developer_mode_option_in_ui(struct AmfiClientHandle *client); + +/** + * Enables developer mode on the device + * + * # Arguments + * * `client` - A valid AmfiClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *amfi_enable_developer_mode(struct AmfiClientHandle *client); + +/** + * Accepts developer mode on the device + * + * # Arguments + * * `client` - A valid AmfiClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + */ +struct IdeviceFfiError *amfi_accept_developer_mode(struct AmfiClientHandle *client); + +/** + * Frees a handle + * + * # Arguments + * * [`handle`] - The handle to free + * + * # Safety + * `handle` must be a valid pointer to the handle that was allocated by this library, + * or NULL (in which case this function does nothing) + */ +void amfi_client_free(struct AmfiClientHandle *handle); /** * Automatically creates and connects to Core Device Proxy, returning a client handle * * # Arguments - * * [`provider`] - A UsbmuxdProvider + * * [`provider`] - An IdeviceProvider * * [`client`] - On success, will be set to point to a newly allocated CoreDeviceProxy handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `provider` must be a valid pointer to a handle allocated by this library * `client` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode core_device_proxy_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct CoreDeviceProxyHandle **client); +struct IdeviceFfiError *core_device_proxy_connect(struct IdeviceProviderHandle *provider, + struct CoreDeviceProxyHandle **client); /** * Automatically creates and connects to Core Device Proxy, returning a client handle @@ -395,14 +843,15 @@ enum IdeviceErrorCode core_device_proxy_connect_usbmuxd(struct UsbmuxdProviderHa * * [`client`] - On success, will be set to point to a newly allocated CoreDeviceProxy handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety - * `socket` must be a valid pointer to a handle allocated by this library + * `socket` must be a valid pointer to a handle allocated by this library. It is consumed and + * may not be used again. * `client` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode core_device_proxy_new(struct IdeviceHandle *socket, - struct CoreDeviceProxyHandle **client); +struct IdeviceFfiError *core_device_proxy_new(struct IdeviceHandle *socket, + struct CoreDeviceProxyHandle **client); /** * Sends data through the CoreDeviceProxy tunnel @@ -413,15 +862,15 @@ enum IdeviceErrorCode core_device_proxy_new(struct IdeviceHandle *socket, * * [`length`] - The length of the data * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer to a handle allocated by this library * `data` must be a valid pointer to at least `length` bytes */ -enum IdeviceErrorCode core_device_proxy_send(struct CoreDeviceProxyHandle *handle, - const uint8_t *data, - uintptr_t length); +struct IdeviceFfiError *core_device_proxy_send(struct CoreDeviceProxyHandle *handle, + const uint8_t *data, + uintptr_t length); /** * Receives data from the CoreDeviceProxy tunnel @@ -433,17 +882,17 @@ enum IdeviceErrorCode core_device_proxy_send(struct CoreDeviceProxyHandle *handl * * [`max_length`] - Maximum number of bytes that can be stored in `data` * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer to a handle allocated by this library * `data` must be a valid pointer to at least `max_length` bytes * `length` must be a valid pointer to a usize */ -enum IdeviceErrorCode core_device_proxy_recv(struct CoreDeviceProxyHandle *handle, - uint8_t *data, - uintptr_t *length, - uintptr_t max_length); +struct IdeviceFfiError *core_device_proxy_recv(struct CoreDeviceProxyHandle *handle, + uint8_t *data, + uintptr_t *length, + uintptr_t max_length); /** * Gets the client parameters from the handshake @@ -451,38 +900,38 @@ enum IdeviceErrorCode core_device_proxy_recv(struct CoreDeviceProxyHandle *handl * # Arguments * * [`handle`] - The CoreDeviceProxy handle * * [`mtu`] - Pointer to store the MTU value - * * [`address`] - Pointer to store the IP address string (must be at least 16 bytes) - * * [`netmask`] - Pointer to store the netmask string (must be at least 16 bytes) + * * [`address`] - Pointer to store the IP address string + * * [`netmask`] - Pointer to store the netmask string * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer to a handle allocated by this library * `mtu` must be a valid pointer to a u16 * `address` and `netmask` must be valid pointers to buffers of at least 16 bytes */ -enum IdeviceErrorCode core_device_proxy_get_client_parameters(struct CoreDeviceProxyHandle *handle, - uint16_t *mtu, - char **address, - char **netmask); +struct IdeviceFfiError *core_device_proxy_get_client_parameters(struct CoreDeviceProxyHandle *handle, + uint16_t *mtu, + char **address, + char **netmask); /** * Gets the server address from the handshake * * # Arguments * * [`handle`] - The CoreDeviceProxy handle - * * [`address`] - Pointer to store the server address string (must be at least 16 bytes) + * * [`address`] - Pointer to store the server address string * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer to a handle allocated by this library * `address` must be a valid pointer to a buffer of at least 16 bytes */ -enum IdeviceErrorCode core_device_proxy_get_server_address(struct CoreDeviceProxyHandle *handle, - char **address); +struct IdeviceFfiError *core_device_proxy_get_server_address(struct CoreDeviceProxyHandle *handle, + char **address); /** * Gets the server RSD port from the handshake @@ -492,14 +941,14 @@ enum IdeviceErrorCode core_device_proxy_get_server_address(struct CoreDeviceProx * * [`port`] - Pointer to store the port number * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer to a handle allocated by this library * `port` must be a valid pointer to a u16 */ -enum IdeviceErrorCode core_device_proxy_get_server_rsd_port(struct CoreDeviceProxyHandle *handle, - uint16_t *port); +struct IdeviceFfiError *core_device_proxy_get_server_rsd_port(struct CoreDeviceProxyHandle *handle, + uint16_t *port); /** * Creates a software TCP tunnel adapter @@ -509,14 +958,14 @@ enum IdeviceErrorCode core_device_proxy_get_server_rsd_port(struct CoreDevicePro * * [`adapter`] - Pointer to store the newly created adapter handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer to a handle allocated by this library, and never used again * `adapter` must be a valid pointer to a location where the handle will be stored */ -enum IdeviceErrorCode core_device_proxy_create_tcp_adapter(struct CoreDeviceProxyHandle *handle, - struct AdapterHandle **adapter); +struct IdeviceFfiError *core_device_proxy_create_tcp_adapter(struct CoreDeviceProxyHandle *handle, + struct AdapterHandle **adapter); /** * Frees a handle @@ -564,18 +1013,19 @@ void debugserver_command_free(struct DebugserverCommandHandle *command); * Creates a new DebugProxyClient * * # Arguments - * * [`socket`] - The socket to use for communication - * * [`handle`] - Pointer to store the newly created DebugProxyClient handle + * * [`provider`] - An adapter created by this library + * * [`handshake`] - An RSD handshake from the same provider * * # Returns - * An error code indicating success or failure + * 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 + * `provider` must be a valid pointer to a handle allocated by this library + * `handshake` must be a valid pointer to a location where the handle will be stored */ -enum IdeviceErrorCode debug_proxy_adapter_new(struct AdapterHandle *socket, - struct DebugProxyAdapterHandle **handle); +struct IdeviceFfiError *debug_proxy_connect_rsd(struct AdapterHandle *provider, + struct RsdHandshakeHandle *handshake, + struct DebugProxyHandle **handle); /** * Frees a DebugProxyClient handle @@ -586,7 +1036,7 @@ enum IdeviceErrorCode debug_proxy_adapter_new(struct AdapterHandle *socket, * # Safety * `handle` must be a valid pointer to a handle allocated by this library or NULL */ -void debug_proxy_free(struct DebugProxyAdapterHandle *handle); +void debug_proxy_free(struct DebugProxyHandle *handle); /** * Sends a command to the debug proxy @@ -597,15 +1047,15 @@ void debug_proxy_free(struct DebugProxyAdapterHandle *handle); * * [`response`] - Pointer to store the response (caller must free) * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` and `command` must be valid pointers * `response` must be a valid pointer to a location where the string will be stored */ -enum IdeviceErrorCode debug_proxy_send_command(struct DebugProxyAdapterHandle *handle, - struct DebugserverCommandHandle *command, - char **response); +struct IdeviceFfiError *debug_proxy_send_command(struct DebugProxyHandle *handle, + struct DebugserverCommandHandle *command, + char **response); /** * Reads a response from the debug proxy @@ -615,14 +1065,13 @@ enum IdeviceErrorCode debug_proxy_send_command(struct DebugProxyAdapterHandle *h * * [`response`] - Pointer to store the response (caller must free) * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer * `response` must be a valid pointer to a location where the string will be stored */ -enum IdeviceErrorCode debug_proxy_read_response(struct DebugProxyAdapterHandle *handle, - char **response); +struct IdeviceFfiError *debug_proxy_read_response(struct DebugProxyHandle *handle, char **response); /** * Sends raw data to the debug proxy @@ -633,15 +1082,15 @@ enum IdeviceErrorCode debug_proxy_read_response(struct DebugProxyAdapterHandle * * * [`len`] - Length of the data * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer * `data` must be a valid pointer to `len` bytes */ -enum IdeviceErrorCode debug_proxy_send_raw(struct DebugProxyAdapterHandle *handle, - const uint8_t *data, - uintptr_t len); +struct IdeviceFfiError *debug_proxy_send_raw(struct DebugProxyHandle *handle, + const uint8_t *data, + uintptr_t len); /** * Reads data from the debug proxy @@ -652,15 +1101,15 @@ enum IdeviceErrorCode debug_proxy_send_raw(struct DebugProxyAdapterHandle *handl * * [`response`] - Pointer to store the response (caller must free) * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer * `response` must be a valid pointer to a location where the string will be stored */ -enum IdeviceErrorCode debug_proxy_read(struct DebugProxyAdapterHandle *handle, - uintptr_t len, - char **response); +struct IdeviceFfiError *debug_proxy_read(struct DebugProxyHandle *handle, + uintptr_t len, + char **response); /** * Sets the argv for the debug proxy @@ -672,17 +1121,17 @@ enum IdeviceErrorCode debug_proxy_read(struct DebugProxyAdapterHandle *handle, * * [`response`] - Pointer to store the response (caller must free) * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer * `argv` must be a valid pointer to `argv_count` C strings or NULL * `response` must be a valid pointer to a location where the string will be stored */ -enum IdeviceErrorCode debug_proxy_set_argv(struct DebugProxyAdapterHandle *handle, - const char *const *argv, - uintptr_t argv_count, - char **response); +struct IdeviceFfiError *debug_proxy_set_argv(struct DebugProxyHandle *handle, + const char *const *argv, + uintptr_t argv_count, + char **response); /** * Sends an ACK to the debug proxy @@ -691,12 +1140,12 @@ enum IdeviceErrorCode debug_proxy_set_argv(struct DebugProxyAdapterHandle *handl * * [`handle`] - The DebugProxyClient handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer */ -enum IdeviceErrorCode debug_proxy_send_ack(struct DebugProxyAdapterHandle *handle); +struct IdeviceFfiError *debug_proxy_send_ack(struct DebugProxyHandle *handle); /** * Sends a NACK to the debug proxy @@ -705,12 +1154,12 @@ enum IdeviceErrorCode debug_proxy_send_ack(struct DebugProxyAdapterHandle *handl * * [`handle`] - The DebugProxyClient handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer */ -enum IdeviceErrorCode debug_proxy_send_nack(struct DebugProxyAdapterHandle *handle); +struct IdeviceFfiError *debug_proxy_send_nack(struct DebugProxyHandle *handle); /** * Sets the ACK mode for the debug proxy @@ -722,57 +1171,32 @@ enum IdeviceErrorCode debug_proxy_send_nack(struct DebugProxyAdapterHandle *hand * # Safety * `handle` must be a valid pointer */ -void debug_proxy_set_ack_mode(struct DebugProxyAdapterHandle *handle, int enabled); +void debug_proxy_set_ack_mode(struct DebugProxyHandle *handle, int enabled); /** - * Returns the underlying socket from a DebugProxyClient - * - * # Arguments - * * [`handle`] - The handle to get the socket from - * * [`adapter`] - The newly allocated ConnectionHandle - * - * # Returns - * An error code indicating success or failure + * Frees the IdeviceFfiError * * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL, and never used again + * `err` must be a struct allocated by this library */ -enum IdeviceErrorCode debug_proxy_adapter_into_inner(struct DebugProxyAdapterHandle *handle, - struct AdapterHandle **adapter); +void idevice_error_free(struct IdeviceFfiError *err); /** * Automatically creates and connects to Installation Proxy, returning a client handle * * # Arguments - * * [`provider`] - A TcpProvider + * * [`provider`] - An IdeviceProvider * * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `provider` must be a valid pointer to a handle allocated by this library * `client` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode heartbeat_connect_tcp(struct TcpProviderHandle *provider, - struct HeartbeatClientHandle **client); - -/** - * Automatically creates and connects to Installation Proxy, returning a client handle - * - * # Arguments - * * [`provider`] - A UsbmuxdProvider - * * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored - */ -enum IdeviceErrorCode heartbeat_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct HeartbeatClientHandle **client); +struct IdeviceFfiError *heartbeat_connect(struct IdeviceProviderHandle *provider, + struct HeartbeatClientHandle **client); /** * Automatically creates and connects to Installation Proxy, returning a client handle @@ -782,14 +1206,15 @@ enum IdeviceErrorCode heartbeat_connect_usbmuxd(struct UsbmuxdProviderHandle *pr * * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety - * `socket` must be a valid pointer to a handle allocated by this library + * `socket` must be a valid pointer to a handle allocated by this library. The socket is consumed, + * and may not be used again. * `client` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode heartbeat_new(struct IdeviceHandle *socket, - struct HeartbeatClientHandle **client); +struct IdeviceFfiError *heartbeat_new(struct IdeviceHandle *socket, + struct HeartbeatClientHandle **client); /** * Sends a polo to the device @@ -798,12 +1223,12 @@ enum IdeviceErrorCode heartbeat_new(struct IdeviceHandle *socket, * * `client` - A valid HeartbeatClient handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `client` must be a valid pointer to a handle allocated by this library */ -enum IdeviceErrorCode heartbeat_send_polo(struct HeartbeatClientHandle *client); +struct IdeviceFfiError *heartbeat_send_polo(struct HeartbeatClientHandle *client); /** * Sends a polo to the device @@ -814,14 +1239,14 @@ enum IdeviceErrorCode heartbeat_send_polo(struct HeartbeatClientHandle *client); * * `new_interval` - A pointer to set the requested marco * * # Returns - * An error code indicating success or failure. + * An IdeviceFfiError on error, null on success. * * # Safety * `client` must be a valid pointer to a handle allocated by this library */ -enum IdeviceErrorCode heartbeat_get_marco(struct HeartbeatClientHandle *client, - uint64_t interval, - uint64_t *new_interval); +struct IdeviceFfiError *heartbeat_get_marco(struct HeartbeatClientHandle *client, + uint64_t interval, + uint64_t *new_interval); /** * Frees a handle @@ -839,138 +1264,349 @@ void heartbeat_client_free(struct HeartbeatClientHandle *handle); * Automatically creates and connects to Installation Proxy, returning a client handle * * # Arguments - * * [`provider`] - A TcpProvider + * * [`provider`] - An IdeviceProvider * * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `provider` must be a valid pointer to a handle allocated by this library * `client` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode installation_proxy_connect_tcp(struct TcpProviderHandle *provider, - struct InstallationProxyClientHandle **client); +struct IdeviceFfiError *installation_proxy_connect_tcp(struct IdeviceProviderHandle *provider, + struct InstallationProxyClientHandle **client); /** * Automatically creates and connects to Installation Proxy, returning a client handle * * # Arguments - * * [`provider`] - A UsbmuxdProvider + * * [`socket`] - An IdeviceSocket handle * * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety - * `provider` must be a valid pointer to a handle allocated by this library + * `socket` must be a valid pointer to a handle allocated by this library. The socket is consumed, + * and may not be used again. * `client` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode installation_proxy_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct InstallationProxyClientHandle **client); +struct IdeviceFfiError *installation_proxy_new(struct IdeviceHandle *socket, + struct InstallationProxyClientHandle **client); /** - * Automatically creates and connects to Installation Proxy, returning a client handle + * Gets installed apps on the device * * # Arguments - * * [`socket`] - An IdeviceSocket handle - * * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle + * * [`client`] - A valid InstallationProxyClient handle + * * [`application_type`] - The application type to filter by (optional, NULL for "Any") + * * [`bundle_identifiers`] - The identifiers to filter by (optional, NULL for all apps) + * * [`out_result`] - On success, will be set to point to a newly allocated array of PlistRef * * # Returns - * An error code indicating success or failure + * 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 + * `client` must be a valid pointer to a handle allocated by this library + * `out_result` must be a valid, non-null pointer to a location where the result will be stored */ -enum IdeviceErrorCode installation_proxy_new(struct IdeviceHandle *socket, - struct InstallationProxyClientHandle **client); +struct IdeviceFfiError *installation_proxy_get_apps(struct InstallationProxyClientHandle *client, + const char *application_type, + const char *const *bundle_identifiers, + size_t bundle_identifiers_len, + void **out_result, + size_t *out_result_len); /** - * Gets installed apps on the device + * Frees a handle + * + * # Arguments + * * [`handle`] - The handle to free + * + * # Safety + * `handle` must be a valid pointer to the handle that was allocated by this library, + * or NULL (in which case this function does nothing) + */ +void installation_proxy_client_free(struct InstallationProxyClientHandle *handle); + +/** + * Installs an application package on the device * * # Arguments - * * `client` - A valid InstallationProxyClient handle - * * `application_type` - The application type to filter by (optional, NULL for "Any") - * * `bundle_identifiers` - The identifiers to filter by (optional, NULL for all apps) - * * `out_result` - On success, will be set to point to a newly allocated array of PlistRef + * * [`client`] - A valid InstallationProxyClient handle + * * [`package_path`] - Path to the .ipa package in the AFC jail + * * [`options`] - Optional installation options as a plist dictionary (can be NULL) * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `client` must be a valid pointer to a handle allocated by this library + * `package_path` must be a valid C string + * `options` must be a valid plist dictionary or NULL + */ +struct IdeviceFfiError *installation_proxy_install(struct InstallationProxyClientHandle *client, + const char *package_path, + void *options); + +/** + * Installs an application package on the device + * + * # Arguments + * * [`client`] - A valid InstallationProxyClient handle + * * [`package_path`] - Path to the .ipa package in the AFC jail + * * [`options`] - Optional installation options as a plist dictionary (can be NULL) + * * [`callback`] - Progress callback function + * * [`context`] - User context to pass to callback + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `package_path` must be a valid C string + * `options` must be a valid plist dictionary or NULL + */ +struct IdeviceFfiError *installation_proxy_install_with_callback(struct InstallationProxyClientHandle *client, + const char *package_path, + void *options, + void (*callback)(uint64_t progress, + void *context), + void *context); + +/** + * Upgrades an existing application on the device + * + * # Arguments + * * [`client`] - A valid InstallationProxyClient handle + * * [`package_path`] - Path to the .ipa package in the AFC jail + * * [`options`] - Optional upgrade options as a plist dictionary (can be NULL) + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `package_path` must be a valid C string + * `options` must be a valid plist dictionary or NULL + */ +struct IdeviceFfiError *installation_proxy_upgrade(struct InstallationProxyClientHandle *client, + const char *package_path, + void *options); + +/** + * Upgrades an existing application on the device + * + * # Arguments + * * [`client`] - A valid InstallationProxyClient handle + * * [`package_path`] - Path to the .ipa package in the AFC jail + * * [`options`] - Optional upgrade options as a plist dictionary (can be NULL) + * * [`callback`] - Progress callback function + * * [`context`] - User context to pass to callback + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `package_path` must be a valid C string + * `options` must be a valid plist dictionary or NULL + */ +struct IdeviceFfiError *installation_proxy_upgrade_with_callback(struct InstallationProxyClientHandle *client, + const char *package_path, + void *options, + void (*callback)(uint64_t progress, + void *context), + void *context); + +/** + * Uninstalls an application from the device + * + * # Arguments + * * [`client`] - A valid InstallationProxyClient handle + * * [`bundle_id`] - Bundle identifier of the application to uninstall + * * [`options`] - Optional uninstall options as a plist dictionary (can be NULL) + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `bundle_id` must be a valid C string + * `options` must be a valid plist dictionary or NULL + */ +struct IdeviceFfiError *installation_proxy_uninstall(struct InstallationProxyClientHandle *client, + const char *bundle_id, + void *options); + +/** + * Uninstalls an application from the device + * + * # Arguments + * * [`client`] - A valid InstallationProxyClient handle + * * [`bundle_id`] - Bundle identifier of the application to uninstall + * * [`options`] - Optional uninstall options as a plist dictionary (can be NULL) + * * [`callback`] - Progress callback function + * * [`context`] - User context to pass to callback + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `bundle_id` must be a valid C string + * `options` must be a valid plist dictionary or NULL + */ +struct IdeviceFfiError *installation_proxy_uninstall_with_callback(struct InstallationProxyClientHandle *client, + const char *bundle_id, + void *options, + void (*callback)(uint64_t progress, + void *context), + void *context); + +/** + * Checks if the device capabilities match the required capabilities + * + * # Arguments + * * [`client`] - A valid InstallationProxyClient handle + * * [`capabilities`] - Array of plist values representing required capabilities + * * [`capabilities_len`] - Length of the capabilities array + * * [`options`] - Optional check options as a plist dictionary (can be NULL) + * * [`out_result`] - Will be set to true if all capabilities are supported, false otherwise + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `capabilities` must be a valid array of plist values or NULL + * `options` must be a valid plist dictionary or NULL + * `out_result` must be a valid pointer to a bool + */ +struct IdeviceFfiError *installation_proxy_check_capabilities_match(struct InstallationProxyClientHandle *client, + void *const *capabilities, + size_t capabilities_len, + void *options, + bool *out_result); + +/** + * Browses installed applications on the device + * + * # Arguments + * * [`client`] - A valid InstallationProxyClient handle + * * [`options`] - Optional browse options as a plist dictionary (can be NULL) + * * [`out_result`] - On success, will be set to point to a newly allocated array of PlistRef + * * [`out_result_len`] - Will be set to the length of the result array + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `options` must be a valid plist dictionary or NULL * `out_result` must be a valid, non-null pointer to a location where the result will be stored + * `out_result_len` must be a valid, non-null pointer to a location where the length will be stored */ -enum IdeviceErrorCode installation_proxy_get_apps(struct InstallationProxyClientHandle *client, - const char *application_type, - const char *const *bundle_identifiers, - size_t bundle_identifiers_len, +struct IdeviceFfiError *installation_proxy_browse(struct InstallationProxyClientHandle *client, + void *options, void **out_result, size_t *out_result_len); /** - * Frees a handle + * Creates a new ProcessControlClient from a RemoteServerClient + * + * # Arguments + * * [`server`] - The RemoteServerClient to use + * * [`handle`] - Pointer to store the newly created ProcessControlClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # 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 *location_simulation_new(struct RemoteServerHandle *server, + struct LocationSimulationHandle **handle); + +/** + * Frees a ProcessControlClient handle * * # Arguments * * [`handle`] - The handle to free * * # Safety - * `handle` must be a valid pointer to the handle that was allocated by this library, - * or NULL (in which case this function does nothing) + * `handle` must be a valid pointer to a handle allocated by this library or NULL */ -void installation_proxy_client_free(struct InstallationProxyClientHandle *handle); +void location_simulation_free(struct LocationSimulationHandle *handle); /** - * Connects to lockdownd service using TCP provider + * Clears the location set * * # Arguments - * * [`provider`] - A TcpProvider - * * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle + * * [`handle`] - The LocationSimulation handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored + * All pointers must be valid or NULL where appropriate */ -enum IdeviceErrorCode lockdownd_connect_tcp(struct TcpProviderHandle *provider, - struct LockdowndClientHandle **client); +struct IdeviceFfiError *location_simulation_clear(struct LocationSimulationHandle *handle); /** - * Connects to lockdownd service using Usbmuxd provider + * Sets the location * * # Arguments - * * [`provider`] - A UsbmuxdProvider + * * [`handle`] - The LocationSimulation handle + * * [`latitude`] - The latitude to set + * * [`longitude`] - The longitude to set + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * All pointers must be valid or NULL where appropriate + */ +struct IdeviceFfiError *location_simulation_set(struct LocationSimulationHandle *handle, + double latitude, + double longitude); + +/** + * Connects to lockdownd service using TCP provider + * + * # Arguments + * * [`provider`] - An IdeviceProvider * * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `provider` must be a valid pointer to a handle allocated by this library * `client` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode lockdownd_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct LockdowndClientHandle **client); +struct IdeviceFfiError *lockdownd_connect(struct IdeviceProviderHandle *provider, + struct LockdowndClientHandle **client); /** * Creates a new LockdowndClient from an existing Idevice connection * * # Arguments - * * [`socket`] - An IdeviceSocket handle + * * [`socket`] - An IdeviceSocket handle. * * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety - * `socket` must be a valid pointer to a handle allocated by this library + * `socket` must be a valid pointer to a handle allocated by this library. The socket is consumed, + * and maybe not be used again. * `client` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode lockdownd_new(struct IdeviceHandle *socket, - struct LockdowndClientHandle **client); +struct IdeviceFfiError *lockdownd_new(struct IdeviceHandle *socket, + struct LockdowndClientHandle **client); /** * Starts a session with lockdownd @@ -980,14 +1616,14 @@ enum IdeviceErrorCode lockdownd_new(struct IdeviceHandle *socket, * * `pairing_file` - An IdevicePairingFile alocated by this library * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `client` must be a valid pointer to a handle allocated by this library * `pairing_file` must be a valid plist_t containing a pairing file */ -enum IdeviceErrorCode lockdownd_start_session(struct LockdowndClientHandle *client, - struct IdevicePairingFile *pairing_file); +struct IdeviceFfiError *lockdownd_start_session(struct LockdowndClientHandle *client, + struct IdevicePairingFile *pairing_file); /** * Starts a service through lockdownd @@ -999,126 +1635,211 @@ enum IdeviceErrorCode lockdownd_start_session(struct LockdowndClientHandle *clie * * `ssl` - Pointer to store whether SSL should be enabled * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `client` must be a valid pointer to a handle allocated by this library * `identifier` must be a valid null-terminated string * `port` and `ssl` must be valid pointers */ -enum IdeviceErrorCode lockdownd_start_service(struct LockdowndClientHandle *client, - const char *identifier, - uint16_t *port, - bool *ssl); +struct IdeviceFfiError *lockdownd_start_service(struct LockdowndClientHandle *client, + const char *identifier, + uint16_t *port, + bool *ssl); /** * Gets a value from lockdownd * * # Arguments * * `client` - A valid LockdowndClient handle - * * `value` - The value to get (null-terminated string) + * * `key` - The value to get (null-terminated string) + * * `domain` - The value to get (null-terminated string) * * `out_plist` - Pointer to store the returned plist value * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `value` must be a valid null-terminated string + * `out_plist` must be a valid pointer to store the plist + */ +struct IdeviceFfiError *lockdownd_get_value(struct LockdowndClientHandle *client, + const char *key, + const char *domain, + void **out_plist); + +/** + * Gets all values from lockdownd + * + * # Arguments + * * `client` - A valid LockdowndClient handle + * * `out_plist` - Pointer to store the returned plist dictionary + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `out_plist` must be a valid pointer to store the plist + */ +struct IdeviceFfiError *lockdownd_get_all_values(struct LockdowndClientHandle *client, + const char *domain, + void **out_plist); + +/** + * Frees a LockdowndClient handle + * + * # Arguments + * * [`handle`] - The handle to free + * + * # Safety + * `handle` must be a valid pointer to the handle that was allocated by this library, + * or NULL (in which case this function does nothing) + */ +void lockdownd_client_free(struct LockdowndClientHandle *handle); + +/** + * Initializes the logger + * + * # Arguments + * * [`console_level`] - The level to log to the file + * * [`file_level`] - The level to log to the file + * * [`file_path`] - If not null, the file to write logs to + * + * ## Log Level + * 0. Disabled + * 1. Error + * 2. Warn + * 3. Info + * 4. Debug + * 5. Trace + * + * # Returns + * 0 for success, -1 if the file couldn't be created, -2 if a logger has been initialized, -3 for invalid path string + * + * # Safety + * Pass a valid CString for file_path. Pass valid log levels according to the enum + */ +enum IdeviceLoggerError idevice_init_logger(enum IdeviceLogLevel console_level, + enum IdeviceLogLevel file_level, + char *file_path); + +/** + * Automatically creates and connects to Misagent, returning a client handle + * + * # Arguments + * * [`provider`] - An IdeviceProvider + * * [`client`] - On success, will be set to point to a newly allocated MisagentClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `provider` must be a valid pointer to a handle allocated by this library + * `client` must be a valid, non-null pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *misagent_connect(struct IdeviceProviderHandle *provider, + struct MisagentClientHandle **client); + +/** + * Installs a provisioning profile on the device + * + * # Arguments + * * [`client`] - A valid MisagentClient handle + * * [`profile_data`] - The provisioning profile data to install + * * [`profile_len`] - Length of the profile data + * + * # Returns + * An IdeviceFfiError on error, null on success * * # Safety * `client` must be a valid pointer to a handle allocated by this library - * `value` must be a valid null-terminated string - * `out_plist` must be a valid pointer to store the plist + * `profile_data` must be a valid pointer to profile data of length `profile_len` */ -enum IdeviceErrorCode lockdownd_get_value(struct LockdowndClientHandle *client, - const char *value, - void **out_plist); +struct IdeviceFfiError *misagent_install(struct MisagentClientHandle *client, + const uint8_t *profile_data, + size_t profile_len); /** - * Gets all values from lockdownd + * Removes a provisioning profile from the device * * # Arguments - * * `client` - A valid LockdowndClient handle - * * `out_plist` - Pointer to store the returned plist dictionary + * * [`client`] - A valid MisagentClient handle + * * [`profile_id`] - The UUID of the profile to remove (C string) * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `client` must be a valid pointer to a handle allocated by this library - * `out_plist` must be a valid pointer to store the plist + * `profile_id` must be a valid C string */ -enum IdeviceErrorCode lockdownd_get_all_values(struct LockdowndClientHandle *client, - void **out_plist); +struct IdeviceFfiError *misagent_remove(struct MisagentClientHandle *client, + const char *profile_id); /** - * Frees a LockdowndClient handle + * Retrieves all provisioning profiles from the device * * # Arguments - * * [`handle`] - The handle to free + * * [`client`] - A valid MisagentClient handle + * * [`out_profiles`] - On success, will be set to point to an array of profile data + * * [`out_profiles_len`] - On success, will be set to the number of profiles + * + * # Returns + * An IdeviceFfiError on error, null on success * * # Safety - * `handle` must be a valid pointer to the handle that was allocated by this library, - * or NULL (in which case this function does nothing) + * `client` must be a valid pointer to a handle allocated by this library + * `out_profiles` must be a valid pointer to store the resulting array + * `out_profiles_len` must be a valid pointer to store the array length */ -void lockdownd_client_free(struct LockdowndClientHandle *handle); +struct IdeviceFfiError *misagent_copy_all(struct MisagentClientHandle *client, + uint8_t ***out_profiles, + size_t **out_profiles_len, + size_t *out_count); /** - * Initializes the logger + * Frees profiles array returned by misagent_copy_all * * # Arguments - * * [`console_level`] - The level to log to the file - * * [`file_level`] - The level to log to the file - * * [`file_path`] - If not null, the file to write logs to - * - * ## Log Level - * 0. Disabled - * 1. Error - * 2. Warn - * 3. Info - * 4. Debug - * 5. Trace - * - * # Returns - * 0 for success, -1 if the file couldn't be created, -2 if a logger has been initialized, -3 for invalid path string + * * [`profiles`] - Array of profile data pointers + * * [`lens`] - Array of profile lengths + * * [`count`] - Number of profiles in the array * * # Safety - * Pass a valid CString for file_path. Pass valid log levels according to the enum + * Must only be called with values returned from misagent_copy_all */ -enum IdeviceLoggerError idevice_init_logger(enum IdeviceLogLevel console_level, - enum IdeviceLogLevel file_level, - char *file_path); +void misagent_free_profiles(uint8_t **profiles, size_t *lens, size_t count); /** - * Connects to the Image Mounter service using a TCP provider + * Frees a misagent client handle * * # Arguments - * * [`provider`] - A TcpProvider - * * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle - * - * # Returns - * An error code indicating success or failure + * * [`handle`] - The handle to free * * # Safety - * `provider` must be a valid pointer to a handle allocated by this library - * `client` must be a valid, non-null pointer to a location where the handle will be stored + * `handle` must be a valid pointer to a handle allocated by this library, + * or NULL (in which case this function does nothing) */ -enum IdeviceErrorCode image_mounter_connect_tcp(struct TcpProviderHandle *provider, - struct ImageMounterHandle **client); +void misagent_client_free(struct MisagentClientHandle *handle); /** - * Connects to the Image Mounter service using a Usbmuxd provider + * Connects to the Image Mounter service using a provider * * # Arguments - * * [`provider`] - A UsbmuxdProvider + * * [`provider`] - An IdeviceProvider * * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `provider` must be a valid pointer to a handle allocated by this library * `client` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode image_mounter_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct ImageMounterHandle **client); +struct IdeviceFfiError *image_mounter_connect(struct IdeviceProviderHandle *provider, + struct ImageMounterHandle **client); /** * Creates a new ImageMounter client from an existing Idevice connection @@ -1128,14 +1849,14 @@ enum IdeviceErrorCode image_mounter_connect_usbmuxd(struct UsbmuxdProviderHandle * * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle * * # Returns - * An error code indicating success or failure + * 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 */ -enum IdeviceErrorCode image_mounter_new(struct IdeviceHandle *socket, - struct ImageMounterHandle **client); +struct IdeviceFfiError *image_mounter_new(struct IdeviceHandle *socket, + struct ImageMounterHandle **client); /** * Frees an ImageMounter handle @@ -1158,15 +1879,15 @@ void image_mounter_free(struct ImageMounterHandle *handle); * * [`devices_len`] - Will be set to the number of devices copied * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `client` must be a valid pointer to a handle allocated by this library * `devices` must be a valid, non-null pointer to a location where the plist will be stored */ -enum IdeviceErrorCode image_mounter_copy_devices(struct ImageMounterHandle *client, - void **devices, - size_t *devices_len); +struct IdeviceFfiError *image_mounter_copy_devices(struct ImageMounterHandle *client, + void **devices, + size_t *devices_len); /** * Looks up an image and returns its signature @@ -1178,17 +1899,17 @@ enum IdeviceErrorCode image_mounter_copy_devices(struct ImageMounterHandle *clie * * [`signature_len`] - Will be set to the length of the signature data * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `client` must be a valid pointer to a handle allocated by this library * `image_type` must be a valid null-terminated C string * `signature` and `signature_len` must be valid pointers */ -enum IdeviceErrorCode image_mounter_lookup_image(struct ImageMounterHandle *client, - const char *image_type, - uint8_t **signature, - size_t *signature_len); +struct IdeviceFfiError *image_mounter_lookup_image(struct ImageMounterHandle *client, + const char *image_type, + uint8_t **signature, + size_t *signature_len); /** * Uploads an image to the device @@ -1202,18 +1923,18 @@ enum IdeviceErrorCode image_mounter_lookup_image(struct ImageMounterHandle *clie * * [`signature_len`] - Length of the signature data * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * All pointers must be valid and non-null * `image_type` must be a valid null-terminated C string */ -enum IdeviceErrorCode image_mounter_upload_image(struct ImageMounterHandle *client, - const char *image_type, - const uint8_t *image, - size_t image_len, - const uint8_t *signature, - size_t signature_len); +struct IdeviceFfiError *image_mounter_upload_image(struct ImageMounterHandle *client, + const char *image_type, + const uint8_t *image, + size_t image_len, + const uint8_t *signature, + size_t signature_len); /** * Mounts an image on the device @@ -1228,19 +1949,19 @@ enum IdeviceErrorCode image_mounter_upload_image(struct ImageMounterHandle *clie * * [`info_plist`] - Pointer to info plist (optional) * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * All pointers must be valid (except optional ones which can be null) * `image_type` must be a valid null-terminated C string */ -enum IdeviceErrorCode image_mounter_mount_image(struct ImageMounterHandle *client, - const char *image_type, - const uint8_t *signature, - size_t signature_len, - const uint8_t *trust_cache, - size_t trust_cache_len, - const void *info_plist); +struct IdeviceFfiError *image_mounter_mount_image(struct ImageMounterHandle *client, + const char *image_type, + const uint8_t *signature, + size_t signature_len, + const uint8_t *trust_cache, + size_t trust_cache_len, + const void *info_plist); /** * Unmounts an image from the device @@ -1250,14 +1971,14 @@ enum IdeviceErrorCode image_mounter_mount_image(struct ImageMounterHandle *clien * * [`mount_path`] - The path where the image is mounted * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `client` must be a valid pointer to a handle allocated by this library * `mount_path` must be a valid null-terminated C string */ -enum IdeviceErrorCode image_mounter_unmount_image(struct ImageMounterHandle *client, - const char *mount_path); +struct IdeviceFfiError *image_mounter_unmount_image(struct ImageMounterHandle *client, + const char *mount_path); /** * Queries the developer mode status @@ -1267,14 +1988,14 @@ enum IdeviceErrorCode image_mounter_unmount_image(struct ImageMounterHandle *cli * * [`status`] - Will be set to the developer mode status (1 = enabled, 0 = disabled) * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `client` must be a valid pointer to a handle allocated by this library * `status` must be a valid pointer */ -enum IdeviceErrorCode image_mounter_query_developer_mode_status(struct ImageMounterHandle *client, - int *status); +struct IdeviceFfiError *image_mounter_query_developer_mode_status(struct ImageMounterHandle *client, + int *status); /** * Mounts a developer image @@ -1287,16 +2008,16 @@ enum IdeviceErrorCode image_mounter_query_developer_mode_status(struct ImageMoun * * [`signature_len`] - Length of the signature data * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * All pointers must be valid and non-null */ -enum IdeviceErrorCode image_mounter_mount_developer(struct ImageMounterHandle *client, - const uint8_t *image, - size_t image_len, - const uint8_t *signature, - size_t signature_len); +struct IdeviceFfiError *image_mounter_mount_developer(struct ImageMounterHandle *client, + const uint8_t *image, + size_t image_len, + const uint8_t *signature, + size_t signature_len); /** * Queries the personalization manifest from the device @@ -1310,18 +2031,18 @@ enum IdeviceErrorCode image_mounter_mount_developer(struct ImageMounterHandle *c * * [`manifest_len`] - Will be set to the length of the manifest data * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * All pointers must be valid and non-null * `image_type` must be a valid null-terminated C string */ -enum IdeviceErrorCode image_mounter_query_personalization_manifest(struct ImageMounterHandle *client, - const char *image_type, - const uint8_t *signature, - size_t signature_len, - uint8_t **manifest, - size_t *manifest_len); +struct IdeviceFfiError *image_mounter_query_personalization_manifest(struct ImageMounterHandle *client, + const char *image_type, + const uint8_t *signature, + size_t signature_len, + uint8_t **manifest, + size_t *manifest_len); /** * Queries the nonce from the device @@ -1333,16 +2054,16 @@ enum IdeviceErrorCode image_mounter_query_personalization_manifest(struct ImageM * * [`nonce_len`] - Will be set to the length of the nonce data * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `client`, `nonce`, and `nonce_len` must be valid pointers * `personalized_image_type` can be NULL */ -enum IdeviceErrorCode image_mounter_query_nonce(struct ImageMounterHandle *client, - const char *personalized_image_type, - uint8_t **nonce, - size_t *nonce_len); +struct IdeviceFfiError *image_mounter_query_nonce(struct ImageMounterHandle *client, + const char *personalized_image_type, + uint8_t **nonce, + size_t *nonce_len); /** * Queries personalization identifiers from the device @@ -1353,15 +2074,15 @@ enum IdeviceErrorCode image_mounter_query_nonce(struct ImageMounterHandle *clien * * [`identifiers`] - Will be set to point to the identifiers plist on success * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `client` and `identifiers` must be valid pointers * `image_type` can be NULL */ -enum IdeviceErrorCode image_mounter_query_personalization_identifiers(struct ImageMounterHandle *client, - const char *image_type, - void **identifiers); +struct IdeviceFfiError *image_mounter_query_personalization_identifiers(struct ImageMounterHandle *client, + const char *image_type, + void **identifiers); /** * Rolls the personalization nonce @@ -1370,12 +2091,12 @@ enum IdeviceErrorCode image_mounter_query_personalization_identifiers(struct Ima * * [`client`] - A valid ImageMounter handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `client` must be a valid pointer to a handle allocated by this library */ -enum IdeviceErrorCode image_mounter_roll_personalization_nonce(struct ImageMounterHandle *client); +struct IdeviceFfiError *image_mounter_roll_personalization_nonce(struct ImageMounterHandle *client); /** * Rolls the cryptex nonce @@ -1384,12 +2105,12 @@ enum IdeviceErrorCode image_mounter_roll_personalization_nonce(struct ImageMount * * [`client`] - A valid ImageMounter handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `client` must be a valid pointer to a handle allocated by this library */ -enum IdeviceErrorCode image_mounter_roll_cryptex_nonce(struct ImageMounterHandle *client); +struct IdeviceFfiError *image_mounter_roll_cryptex_nonce(struct ImageMounterHandle *client); /** * Mounts a personalized developer image @@ -1407,24 +2128,24 @@ enum IdeviceErrorCode image_mounter_roll_cryptex_nonce(struct ImageMounterHandle * * [`unique_chip_id`] - The device's unique chip ID * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * All pointers must be valid (except optional ones which can be null) */ -enum IdeviceErrorCode image_mounter_mount_personalized_usbmuxd(struct ImageMounterHandle *client, - struct UsbmuxdProviderHandle *provider, - const uint8_t *image, - size_t image_len, - const uint8_t *trust_cache, - size_t trust_cache_len, - const uint8_t *build_manifest, - size_t build_manifest_len, - const void *info_plist, - uint64_t unique_chip_id); +struct IdeviceFfiError *image_mounter_mount_personalized(struct ImageMounterHandle *client, + struct IdeviceProviderHandle *provider, + const uint8_t *image, + size_t image_len, + const uint8_t *trust_cache, + size_t trust_cache_len, + const uint8_t *build_manifest, + size_t build_manifest_len, + const void *info_plist, + uint64_t unique_chip_id); /** - * Mounts a personalized developer image + * Mounts a personalized developer image with progress callback * * # Arguments * * [`client`] - A valid ImageMounter handle @@ -1437,99 +2158,131 @@ enum IdeviceErrorCode image_mounter_mount_personalized_usbmuxd(struct ImageMount * * [`build_manifest_len`] - Length of the build manifest data * * [`info_plist`] - Pointer to info plist (optional) * * [`unique_chip_id`] - The device's unique chip ID + * * [`callback`] - Progress callback function + * * [`context`] - User context to pass to callback * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * All pointers must be valid (except optional ones which can be null) */ -enum IdeviceErrorCode image_mounter_mount_personalized_tcp(struct ImageMounterHandle *client, - struct TcpProviderHandle *provider, - const uint8_t *image, - size_t image_len, - const uint8_t *trust_cache, - size_t trust_cache_len, - const uint8_t *build_manifest, - size_t build_manifest_len, - const void *info_plist, - uint64_t unique_chip_id); +struct IdeviceFfiError *image_mounter_mount_personalized_with_callback(struct ImageMounterHandle *client, + struct IdeviceProviderHandle *provider, + const uint8_t *image, + size_t image_len, + const uint8_t *trust_cache, + size_t trust_cache_len, + const uint8_t *build_manifest, + size_t build_manifest_len, + const void *info_plist, + uint64_t unique_chip_id, + void (*callback)(size_t progress, + size_t total, + void *context), + void *context); /** - * Mounts a personalized developer image with progress callback + * Connects to the relay with the given provider * * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`provider`] - A valid provider handle - * * [`image`] - Pointer to the image data - * * [`image_len`] - Length of the image data - * * [`trust_cache`] - Pointer to the trust cache data - * * [`trust_cache_len`] - Length of the trust cache data - * * [`build_manifest`] - Pointer to the build manifest data - * * [`build_manifest_len`] - Length of the build manifest data - * * [`info_plist`] - Pointer to info plist (optional) - * * [`unique_chip_id`] - The device's unique chip ID - * * [`callback`] - Progress callback function - * * [`context`] - User context to pass to callback + * * [`provider`] - A provider created by this library + * * [`client`] - A pointer where the handle will be allocated * * # Returns - * An error code indicating success or failure + * 0 for success, an *mut IdeviceFfiError otherwise * * # Safety - * All pointers must be valid (except optional ones which can be null) + * None of the arguments can be null. Provider must be allocated by this library. */ -enum IdeviceErrorCode image_mounter_mount_personalized_usbmuxd_with_callback(struct ImageMounterHandle *client, - struct UsbmuxdProviderHandle *provider, - const uint8_t *image, - size_t image_len, - const uint8_t *trust_cache, - size_t trust_cache_len, - const uint8_t *build_manifest, - size_t build_manifest_len, - const void *info_plist, - uint64_t unique_chip_id, - void (*callback)(size_t progress, - size_t total, - void *context), - void *context); +struct IdeviceFfiError *os_trace_relay_connect(struct IdeviceProviderHandle *provider, + struct OsTraceRelayClientHandle **client); /** - * Mounts a personalized developer image with progress callback + * Frees the relay client * * # Arguments - * * [`client`] - A valid ImageMounter handle - * * [`provider`] - A valid provider handle - * * [`image`] - Pointer to the image data - * * [`image_len`] - Length of the image data - * * [`trust_cache`] - Pointer to the trust cache data - * * [`trust_cache_len`] - Length of the trust cache data - * * [`build_manifest`] - Pointer to the build manifest data - * * [`build_manifest_len`] - Length of the build manifest data - * * [`info_plist`] - Pointer to info plist (optional) - * * [`unique_chip_id`] - The device's unique chip ID - * * [`callback`] - Progress callback function - * * [`context`] - User context to pass to callback + * * [`handle`] - The relay client handle + * + * # Safety + * The handle must be allocated by this library + */ +void os_trace_relay_free(struct OsTraceRelayClientHandle *handle); + +/** + * Creates a handle and starts receiving logs + * + * # Arguments + * * [`client`] - The relay client handle + * * [`receiver`] - A pointer to allocate the new handle to + * * [`pid`] - An optional pointer to a PID to get logs for. May be null. * * # Returns - * An error code indicating success or failure + * 0 for success, an *mut IdeviceFfiError otherwise * * # Safety - * All pointers must be valid (except optional ones which can be null) + * The handle must be allocated by this library. It is consumed, and must never be used again. + */ +struct IdeviceFfiError *os_trace_relay_start_trace(struct OsTraceRelayClientHandle *client, + struct OsTraceRelayReceiverHandle **receiver, + const uint32_t *pid); + +/** + * Frees the receiver handle + * + * # Arguments + * * [`handle`] - The relay receiver client handle + * + * # Safety + * The handle must be allocated by this library. It is consumed, and must never be used again. + */ +void os_trace_relay_receiver_free(struct OsTraceRelayReceiverHandle *handle); + +/** + * Gets the PID list from the device + * + * # Arguments + * * [`client`] - The relay receiver client handle + * * [`list`] - A pointer to allocate a list of PIDs to + * + * # Returns + * 0 for success, an *mut IdeviceFfiError otherwise + * + * # Safety + * The handle must be allocated by this library. */ -enum IdeviceErrorCode image_mounter_mount_personalized_tcp_with_callback(struct ImageMounterHandle *client, - struct TcpProviderHandle *provider, - const uint8_t *image, - size_t image_len, - const uint8_t *trust_cache, - size_t trust_cache_len, - const uint8_t *build_manifest, - size_t build_manifest_len, - const void *info_plist, - uint64_t unique_chip_id, - void (*callback)(size_t progress, - size_t total, - void *context), - void *context); +struct IdeviceFfiError *os_trace_relay_get_pid_list(struct OsTraceRelayClientHandle *client, + struct Vec_u64 **list); + +/** + * Gets the next log from the relay + * + * # Arguments + * * [`client`] - The relay receiver client handle + * * [`log`] - A pointer to allocate the new log + * + * # Returns + * 0 for success, an *mut IdeviceFfiError otherwise + * + * # Safety + * The handle must be allocated by this library. + */ +struct IdeviceFfiError *os_trace_relay_next(struct OsTraceRelayReceiverHandle *client, + struct OsTraceLog **log); + +/** + * Frees a log received from the relay + * + * # Arguments + * * [`log`] - The log to free + * + * # Returns + * 0 for success, an *mut IdeviceFfiError otherwise + * + * # Safety + * The log must be allocated by this library. It is consumed and must not be used again. + */ +void os_trace_relay_free_log(struct OsTraceLog *log); /** * Reads a pairing file from the specified path @@ -1539,14 +2292,14 @@ enum IdeviceErrorCode image_mounter_mount_personalized_tcp_with_callback(struct * * [`pairing_file`] - On success, will be set to point to a newly allocated pairing file instance * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `path` must be a valid null-terminated C string * `pairing_file` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode idevice_pairing_file_read(const char *path, - struct IdevicePairingFile **pairing_file); +struct IdeviceFfiError *idevice_pairing_file_read(const char *path, + struct IdevicePairingFile **pairing_file); /** * Parses a pairing file from a byte buffer @@ -1557,15 +2310,15 @@ enum IdeviceErrorCode idevice_pairing_file_read(const char *path, * * [`pairing_file`] - On success, will be set to point to a newly allocated pairing file instance * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `data` must be a valid pointer to a buffer of at least `size` bytes * `pairing_file` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode idevice_pairing_file_from_bytes(const uint8_t *data, - uintptr_t size, - struct IdevicePairingFile **pairing_file); +struct IdeviceFfiError *idevice_pairing_file_from_bytes(const uint8_t *data, + uintptr_t size, + struct IdevicePairingFile **pairing_file); /** * Serializes a pairing file to XML format @@ -1576,16 +2329,16 @@ enum IdeviceErrorCode idevice_pairing_file_from_bytes(const uint8_t *data, * * [`size`] - On success, will be set to the size of the allocated buffer * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `pairing_file` must be a valid, non-null pointer to a pairing file instance * `data` must be a valid, non-null pointer to a location where the buffer pointer will be stored * `size` must be a valid, non-null pointer to a location where the buffer size will be stored */ -enum IdeviceErrorCode idevice_pairing_file_serialize(const struct IdevicePairingFile *pairing_file, - uint8_t **data, - uintptr_t *size); +struct IdeviceFfiError *idevice_pairing_file_serialize(const struct IdevicePairingFile *pairing_file, + uint8_t **data, + uintptr_t *size); /** * Frees a pairing file instance @@ -1607,14 +2360,14 @@ void idevice_pairing_file_free(struct IdevicePairingFile *pairing_file); * * [`handle`] - Pointer to store the newly created ProcessControlClient handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # 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 */ -enum IdeviceErrorCode process_control_new(struct RemoteServerAdapterHandle *server, - struct ProcessControlAdapterHandle **handle); +struct IdeviceFfiError *process_control_new(struct RemoteServerHandle *server, + struct ProcessControlHandle **handle); /** * Frees a ProcessControlClient handle @@ -1625,7 +2378,7 @@ enum IdeviceErrorCode process_control_new(struct RemoteServerAdapterHandle *serv * # Safety * `handle` must be a valid pointer to a handle allocated by this library or NULL */ -void process_control_free(struct ProcessControlAdapterHandle *handle); +void process_control_free(struct ProcessControlHandle *handle); /** * Launches an application on the device @@ -1640,20 +2393,20 @@ void process_control_free(struct ProcessControlAdapterHandle *handle); * * [`pid`] - Pointer to store the process ID of the launched app * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * All pointers must be valid or NULL where appropriate */ -enum IdeviceErrorCode process_control_launch_app(struct ProcessControlAdapterHandle *handle, - const char *bundle_id, - const char *const *env_vars, - uintptr_t env_vars_count, - const char *const *arguments, - uintptr_t arguments_count, - bool start_suspended, - bool kill_existing, - uint64_t *pid); +struct IdeviceFfiError *process_control_launch_app(struct ProcessControlHandle *handle, + const char *bundle_id, + const char *const *env_vars, + uintptr_t env_vars_count, + const char *const *arguments, + uintptr_t arguments_count, + bool start_suspended, + bool kill_existing, + uint64_t *pid); /** * Kills a running process @@ -1663,13 +2416,12 @@ enum IdeviceErrorCode process_control_launch_app(struct ProcessControlAdapterHan * * [`pid`] - The process ID to kill * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer to a handle allocated by this library */ -enum IdeviceErrorCode process_control_kill_app(struct ProcessControlAdapterHandle *handle, - uint64_t pid); +struct IdeviceFfiError *process_control_kill_app(struct ProcessControlHandle *handle, uint64_t pid); /** * Disables memory limits for a process @@ -1679,13 +2431,13 @@ enum IdeviceErrorCode process_control_kill_app(struct ProcessControlAdapterHandl * * [`pid`] - The process ID to modify * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer to a handle allocated by this library */ -enum IdeviceErrorCode process_control_disable_memory_limit(struct ProcessControlAdapterHandle *handle, - uint64_t pid); +struct IdeviceFfiError *process_control_disable_memory_limit(struct ProcessControlHandle *handle, + uint64_t pid); /** * Creates a TCP provider for idevice @@ -1697,30 +2449,30 @@ enum IdeviceErrorCode process_control_disable_memory_limit(struct ProcessControl * * [`provider`] - A pointer to a newly allocated provider * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `ip` must be a valid sockaddr - * `pairing_file` must never be used again + * `pairing_file` is consumed must never be used again * `label` must be a valid Cstr * `provider` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode idevice_tcp_provider_new(const struct sockaddr *ip, - struct IdevicePairingFile *pairing_file, - const char *label, - struct TcpProviderHandle **provider); +struct IdeviceFfiError *idevice_tcp_provider_new(const struct sockaddr *ip, + struct IdevicePairingFile *pairing_file, + const char *label, + struct IdeviceProviderHandle **provider); /** - * Frees a TcpProvider handle + * Frees an IdeviceProvider handle * * # Arguments * * [`provider`] - The provider handle to free * * # Safety - * `provider` must be a valid pointer to a TcpProvider handle that was allocated by this library, - * or NULL (in which case this function does nothing) + * `provider` must be a valid pointer to a IdeviceProvider handle that was allocated this library + * or NULL (in which case this function does nothing) */ -void tcp_provider_free(struct TcpProviderHandle *provider); +void idevice_provider_free(struct IdeviceProviderHandle *provider); /** * Creates a usbmuxd provider for idevice @@ -1730,55 +2482,60 @@ void tcp_provider_free(struct TcpProviderHandle *provider); * * [`tag`] - The tag returned in usbmuxd responses * * [`udid`] - The UDID of the device to connect to * * [`device_id`] - The muxer ID of the device to connect to - * * [`pairing_file`] - The pairing file handle to use * * [`label`] - The label to use with the connection * * [`provider`] - A pointer to a newly allocated provider * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `addr` must be a valid pointer to UsbmuxdAddrHandle created by this library, and never used again * `udid` must be a valid CStr - * `pairing_file` must never be used again * `label` must be a valid Cstr * `provider` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode usbmuxd_provider_new(struct UsbmuxdAddrHandle *addr, - uint32_t tag, - const char *udid, - uint32_t device_id, - const char *label, - struct UsbmuxdProviderHandle **provider); +struct IdeviceFfiError *usbmuxd_provider_new(struct UsbmuxdAddrHandle *addr, + uint32_t tag, + const char *udid, + uint32_t device_id, + const char *label, + struct IdeviceProviderHandle **provider); /** - * Frees a UsbmuxdProvider handle + * Creates a new RemoteServerClient from a ReadWrite connection * * # Arguments - * * [`provider`] - The provider handle to free + * * [`socket`] - The connection to use for communication, an object that implements ReadWrite + * * [`handle`] - Pointer to store the newly created RemoteServerClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success * * # Safety - * `provider` must be a valid pointer to a UsbmuxdProvider handle that was allocated by this library, - * or NULL (in which case this function does nothing) + * `socket` must be a valid pointer to a handle allocated by this library. It is consumed and may + * not be used again. + * `handle` must be a valid pointer to a location where the handle will be stored */ -void usbmuxd_provider_free(struct UsbmuxdProviderHandle *provider); +struct IdeviceFfiError *remote_server_new(struct ReadWriteOpaque *socket, + struct RemoteServerHandle **handle); /** - * Creates a new RemoteServerClient from a ReadWrite connection + * Creates a new RemoteServerClient from a handshake and adapter * * # Arguments - * * [`connection`] - The connection to use for communication - * * [`handle`] - Pointer to store the newly created RemoteServerClient handle + * * [`provider`] - An adapter created by this library + * * [`handshake`] - An RSD handshake from the same provider * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety - * `connection` 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 + * `provider` must be a valid pointer to a handle allocated by this library + * `handshake` must be a valid pointer to a location where the handle will be stored */ -enum IdeviceErrorCode remote_server_adapter_new(struct AdapterHandle *adapter, - struct RemoteServerAdapterHandle **handle); +struct IdeviceFfiError *remote_server_connect_rsd(struct AdapterHandle *provider, + struct RsdHandshakeHandle *handshake, + struct RemoteServerHandle **handle); /** * Frees a RemoteServerClient handle @@ -1789,127 +2546,269 @@ enum IdeviceErrorCode remote_server_adapter_new(struct AdapterHandle *adapter, * # Safety * `handle` must be a valid pointer to a handle allocated by this library or NULL */ -void remote_server_free(struct RemoteServerAdapterHandle *handle); +void remote_server_free(struct RemoteServerHandle *handle); /** - * Returns the underlying connection from a RemoteServerClient + * Creates a new RSD handshake from a ReadWrite connection * * # Arguments - * * [`handle`] - The handle to get the connection from - * * [`connection`] - The newly allocated ConnectionHandle + * * [`socket`] - The connection to use for communication + * * [`handle`] - Pointer to store the newly created RsdHandshake handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL, and never used again + * `socket` must be a valid pointer to a ReadWrite handle allocated by this library. It is + * consumed and may not be used again. + * `handle` must be a valid pointer to a location where the handle will be stored */ -enum IdeviceErrorCode remote_server_adapter_into_inner(struct RemoteServerAdapterHandle *handle, - struct AdapterHandle **connection); +struct IdeviceFfiError *rsd_handshake_new(struct ReadWriteOpaque *socket, + struct RsdHandshakeHandle **handle); /** - * Creates a new XPCDevice from an adapter + * Gets the protocol version from the RSD handshake * * # Arguments - * * [`adapter`] - The adapter to use for communication - * * [`device`] - Pointer to store the newly created XPCDevice handle + * * [`handle`] - A valid RsdHandshake handle + * * [`version`] - Pointer to store the protocol version * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety - * `adapter` must be a valid pointer to a handle allocated by this library - * `device` must be a valid pointer to a location where the handle will be stored + * `handle` must be a valid pointer to a handle allocated by this library + * `version` must be a valid pointer to store the version */ -enum IdeviceErrorCode xpc_device_new(struct AdapterHandle *adapter, - struct XPCDeviceAdapterHandle **device); +struct IdeviceFfiError *rsd_get_protocol_version(struct RsdHandshakeHandle *handle, + size_t *version); /** - * Frees an XPCDevice handle + * Gets the UUID from the RSD handshake * * # Arguments - * * [`handle`] - The handle to free + * * [`handle`] - A valid RsdHandshake handle + * * [`uuid`] - Pointer to store the UUID string (caller must free with rsd_free_string) + * + * # Returns + * An IdeviceFfiError on error, null on success * * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL + * `handle` must be a valid pointer to a handle allocated by this library + * `uuid` must be a valid pointer to store the string pointer + */ +struct IdeviceFfiError *rsd_get_uuid(struct RsdHandshakeHandle *handle, char **uuid); + +/** + * Gets all available services from the RSD handshake + * + * # Arguments + * * [`handle`] - A valid RsdHandshake handle + * * [`services`] - Pointer to store the services array + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + * `services` must be a valid pointer to store the services array + * Caller must free the returned array with rsd_free_services + */ +struct IdeviceFfiError *rsd_get_services(struct RsdHandshakeHandle *handle, + struct CRsdServiceArray **services); + +/** + * Checks if a specific service is available + * + * # Arguments + * * [`handle`] - A valid RsdHandshake handle + * * [`service_name`] - Name of the service to check for + * * [`available`] - Pointer to store the availability result + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `handle` must be a valid pointer to a handle allocated by this library + * `service_name` must be a valid C string + * `available` must be a valid pointer to store the boolean result */ -void xpc_device_free(struct XPCDeviceAdapterHandle *handle); +struct IdeviceFfiError *rsd_service_available(struct RsdHandshakeHandle *handle, + const char *service_name, + bool *available); /** - * Gets a service by name from the XPCDevice + * Gets information about a specific service * * # Arguments - * * [`handle`] - The XPCDevice handle - * * [`service_name`] - The name of the service to get - * * [`service`] - Pointer to store the newly created XPCService handle + * * [`handle`] - A valid RsdHandshake handle + * * [`service_name`] - Name of the service to get info for + * * [`service_info`] - Pointer to store the service information * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `handle` must be a valid pointer to a handle allocated by this library - * `service_name` must be a valid null-terminated C string - * `service` must be a valid pointer to a location where the handle will be stored + * `service_name` must be a valid C string + * `service_info` must be a valid pointer to store the service info + * Caller must free the returned service with rsd_free_service */ -enum IdeviceErrorCode xpc_device_get_service(struct XPCDeviceAdapterHandle *handle, +struct IdeviceFfiError *rsd_get_service_info(struct RsdHandshakeHandle *handle, const char *service_name, - struct XPCServiceHandle **service); + struct CRsdService **service_info); + +/** + * Frees a string returned by RSD functions + * + * # Arguments + * * [`string`] - The string to free + * + * # Safety + * Must only be called with strings returned from RSD functions + */ +void rsd_free_string(char *string); + +/** + * Frees a single service returned by rsd_get_service_info + * + * # Arguments + * * [`service`] - The service to free + * + * # Safety + * Must only be called with services returned from rsd_get_service_info + */ +void rsd_free_service(struct CRsdService *service); /** - * Returns the adapter in the RemoteXPC Device + * Frees services array returned by rsd_get_services * * # Arguments - * * [`handle`] - The handle to get the adapter from - * * [`adapter`] - The newly allocated AdapterHandle + * * [`services`] - The services array to free * * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL, and never used again + * Must only be called with arrays returned from rsd_get_services */ -enum IdeviceErrorCode xpc_device_adapter_into_inner(struct XPCDeviceAdapterHandle *handle, - struct AdapterHandle **adapter); +void rsd_free_services(struct CRsdServiceArray *services); /** - * Frees an XPCService handle + * Frees an RSD handshake handle * * # Arguments * * [`handle`] - The handle to free * * # Safety - * `handle` must be a valid pointer to a handle allocated by this library or NULL + * `handle` must be a valid pointer to a handle allocated by this library, + * or NULL (in which case this function does nothing) */ -void xpc_service_free(struct XPCServiceHandle *handle); +void rsd_handshake_free(struct RsdHandshakeHandle *handle); /** - * Gets the list of available service names + * Connects to the Springboard service using a provider * * # Arguments - * * [`handle`] - The XPCDevice handle - * * [`names`] - Pointer to store the array of service names - * * [`count`] - Pointer to store the number of services + * * [`provider`] - An IdeviceProvider + * * [`client`] - On success, will be set to point to a newly allocated SpringBoardServicesClient handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety - * `handle` must be a valid pointer to a handle allocated by this library - * `names` must be a valid pointer to a location where the array will be stored - * `count` must be a valid pointer to a location where the count will be stored + * `provider` must be a valid pointer to a handle allocated by this library + * `client` must be a valid, non-null pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *springboard_services_connect(struct IdeviceProviderHandle *provider, + struct SpringBoardServicesClientHandle **client); + +/** + * Creates a new SpringBoardServices client from an existing Idevice connection + * + * # Arguments + * * [`socket`] - An IdeviceSocket handle + * * [`client`] - On success, will be set to point to a newly allocated SpringBoardServicesClient handle + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `socket` must be a valid pointer to a handle allocated by this library. The socket is consumed, + * and may not be used again. + * `client` must be a valid, non-null pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *springboard_services_new(struct IdeviceHandle *socket, + struct SpringBoardServicesClientHandle **client); + +/** + * Gets the icon of the specified app by bundle identifier + * + * # Arguments + * * `client` - A valid SpringBoardServicesClient handle + * * `bundle_identifier` - The identifiers of the app to get icon + * * `out_result` - On success, will be set to point to a newly allocated png data + * + * # Returns + * An IdeviceFfiError on error, null on success + * + * # Safety + * `client` must be a valid pointer to a handle allocated by this library + * `out_result` must be a valid, non-null pointer to a location where the result will be stored + */ +struct IdeviceFfiError *springboard_services_get_icon(struct SpringBoardServicesClientHandle *client, + const char *bundle_identifier, + void **out_result, + size_t *out_result_len); + +/** + * Frees an SpringBoardServicesClient handle + * + * # Arguments + * * [`handle`] - The handle to free + * + * # Safety + * `handle` must be a valid pointer to the handle that was allocated by this library, + * or NULL (in which case this function does nothing) + */ +void springboard_services_free(struct SpringBoardServicesClientHandle *handle); + +/** + * Automatically creates and connects to syslog relay, returning a client handle + * + * # Arguments + * * [`provider`] - An IdeviceProvider + * * [`client`] - On success, will be set to point to a newly allocated SyslogRelayClient handle + * + * # Safety + * `provider` must be a valid pointer to a handle allocated by this library + * `client` must be a valid, non-null pointer to a location where the handle will be stored + */ +struct IdeviceFfiError *syslog_relay_connect_tcp(struct IdeviceProviderHandle *provider, + struct SyslogRelayClientHandle **client); + +/** + * Frees a handle + * + * # Arguments + * * [`handle`] - The handle to free + * + * # Safety + * `handle` must be a valid pointer to the handle that was allocated by this library, + * or NULL (in which case this function does nothing) */ -enum IdeviceErrorCode xpc_device_get_service_names(struct XPCDeviceAdapterHandle *handle, - char ***names, - uintptr_t *count); +void syslog_relay_client_free(struct SyslogRelayClientHandle *handle); /** - * Frees a list of service names + * Gets the next log message from the relay * * # Arguments - * * [`names`] - The array of service names to free - * * [`count`] - The number of services in the array + * * [`client`] - The SyslogRelayClient handle + * * [`log_message`] - On success a newly allocated cstring will be set to point to the log message * * # Safety - * `names` must be a valid pointer to an array of `count` C strings + * `client` must be a valid pointer to a handle allocated by this library + * `log_message` must be a valid, non-null pointer to a location where the log message will be stored */ -void xpc_device_free_service_names(char **names, uintptr_t count); +struct IdeviceFfiError *syslog_relay_next(struct SyslogRelayClientHandle *client, + char **log_message); /** * Connects to a usbmuxd instance over TCP @@ -1921,16 +2820,16 @@ void xpc_device_free_service_names(char **names, uintptr_t count); * * [`usbmuxd_connection`] - On success, will be set to point to a newly allocated UsbmuxdConnection handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `addr` must be a valid sockaddr * `usbmuxd_connection` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode idevice_usbmuxd_new_tcp_connection(const struct sockaddr *addr, - socklen_t addr_len, - uint32_t tag, - struct UsbmuxdConnectionHandle **usbmuxd_connection); +struct IdeviceFfiError *idevice_usbmuxd_new_tcp_connection(const struct sockaddr *addr, + socklen_t addr_len, + uint32_t tag, + struct UsbmuxdConnectionHandle **usbmuxd_connection); /** * Connects to a usbmuxd instance over unix socket @@ -1941,15 +2840,15 @@ enum IdeviceErrorCode idevice_usbmuxd_new_tcp_connection(const struct sockaddr * * * [`usbmuxd_connection`] - On success, will be set to point to a newly allocated UsbmuxdConnection handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `addr` must be a valid CStr * `usbmuxd_connection` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode idevice_usbmuxd_new_unix_socket_connection(const char *addr, - uint32_t tag, - struct UsbmuxdConnectionHandle **usbmuxd_connection); +struct IdeviceFfiError *idevice_usbmuxd_new_unix_socket_connection(const char *addr, + uint32_t tag, + struct UsbmuxdConnectionHandle **usbmuxd_connection); /** * Frees a UsbmuxdConnection handle @@ -1972,15 +2871,15 @@ void idevice_usbmuxd_connection_free(struct UsbmuxdConnectionHandle *usbmuxd_con * * [`usbmuxd_addr`] - On success, will be set to point to a newly allocated UsbmuxdAddr handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `addr` must be a valid sockaddr * `usbmuxd_Addr` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode idevice_usbmuxd_tcp_addr_new(const struct sockaddr *addr, - socklen_t addr_len, - struct UsbmuxdAddrHandle **usbmuxd_addr); +struct IdeviceFfiError *idevice_usbmuxd_tcp_addr_new(const struct sockaddr *addr, + socklen_t addr_len, + struct UsbmuxdAddrHandle **usbmuxd_addr); /** * Creates a new UsbmuxdAddr struct with a unix socket @@ -1990,14 +2889,14 @@ enum IdeviceErrorCode idevice_usbmuxd_tcp_addr_new(const struct sockaddr *addr, * * [`usbmuxd_addr`] - On success, will be set to point to a newly allocated UsbmuxdAddr handle * * # Returns - * An error code indicating success or failure + * An IdeviceFfiError on error, null on success * * # Safety * `addr` must be a valid CStr * `usbmuxd_addr` must be a valid, non-null pointer to a location where the handle will be stored */ -enum IdeviceErrorCode idevice_usbmuxd_unix_addr_new(const char *addr, - struct UsbmuxdAddrHandle **usbmuxd_addr); +struct IdeviceFfiError *idevice_usbmuxd_unix_addr_new(const char *addr, + struct UsbmuxdAddrHandle **usbmuxd_addr); /** * Frees a UsbmuxdAddr handle @@ -2011,35 +2910,4 @@ enum IdeviceErrorCode idevice_usbmuxd_unix_addr_new(const char *addr, */ void idevice_usbmuxd_addr_free(struct UsbmuxdAddrHandle *usbmuxd_addr); -enum IdeviceErrorCode springboard_services_connect_tcp(struct TcpProviderHandle *provider, - struct SpringBoardServicesClientHandle **client); - -enum IdeviceErrorCode springboard_services_connect_usbmuxd(struct UsbmuxdProviderHandle *provider, - struct SpringBoardServicesClientHandle **client); - -enum IdeviceErrorCode springboard_services_new(struct IdeviceHandle *socket, - struct SpringBoardServicesClientHandle **client); - -/** - * Gets the icon of the specified app by bundle identifier - * - * # Arguments - * * `client` - A valid SpringBoardServicesClient handle - * * `bundle_identifier` - The identifiers of the app to get icon - * * `out_result` - On success, will be set to point to a newly allocated png data - * - * # Returns - * An error code indicating success or failure - * - * # Safety - * `client` must be a valid pointer to a handle allocated by this library - * `out_result` must be a valid, non-null pointer to a location where the result will be stored - */ -enum IdeviceErrorCode springboard_services_get_icon(struct SpringBoardServicesClientHandle *client, - const char *bundle_identifier, - void **out_result, - size_t *out_result_len); - -void springboard_services_free(struct SpringBoardServicesClientHandle *handle); - #endif diff --git a/StikJIT/idevice/jit.c b/StikJIT/idevice/jit.c index 218efd26..643c2aff 100644 --- a/StikJIT/idevice/jit.c +++ b/StikJIT/idevice/jit.c @@ -18,473 +18,298 @@ #include "jit.h" -int debug_app(TcpProviderHandle* tcp_provider, const char *bundle_id, LogFuncC logger) { +IdeviceFfiError* debug_proxy_send_command2(struct DebugProxyHandle *handle, struct DebugserverCommandHandle *command, char **response) { + IdeviceFfiError* err = debug_proxy_send_command(handle, command, response); + if(err) { + return err; + } + for(int i = 0; i < 10; ++i) { + if(*response) { + return err; + } + debug_proxy_read_response(handle, response); + } + return err; +} + +void runDebugServerCommand(int pid, DebugProxyHandle* debug_proxy, LogFuncC logger, DebugAppCallback callback) { + // enable QStartNoAckMode + char *disableResponse = NULL; + debug_proxy_send_ack(debug_proxy); + debug_proxy_send_ack(debug_proxy); + DebugserverCommandHandle *disableAckCommand = debugserver_command_new("QStartNoAckMode", NULL, 0); + IdeviceFfiError* err = debug_proxy_send_command(debug_proxy, disableAckCommand, &disableResponse); + debugserver_command_free(disableAckCommand); + logger("QStartNoAckMode result = %s, err = %d", disableResponse, err); + idevice_string_free(disableResponse); + debug_proxy_set_ack_mode(debug_proxy, false); + + if(callback) { + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + callback(pid, debug_proxy, semaphore); + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + err = debug_proxy_send_raw(debug_proxy, "\x03", 1); + } else { + // Send vAttach command with PID in hex + char attach_command[64]; + snprintf(attach_command, sizeof(attach_command), "vAttach;%" PRIx64, pid); + + DebugserverCommandHandle *attach_cmd = debugserver_command_new(attach_command, NULL, 0); + if (attach_cmd == NULL) { + logger("Failed to create attach command"); + return; + } + + char *attach_response = NULL; + err = debug_proxy_send_command2(debug_proxy, attach_cmd, &attach_response); + debugserver_command_free(attach_cmd); + + if (err) { + logger("Failed to attach to process: %d", err); + } else if (attach_response != NULL) { + logger("Attach response: %s", attach_response); + idevice_string_free(attach_response); + } + + } + + // Send detach command + DebugserverCommandHandle *detach_cmd = debugserver_command_new("D", NULL, 0); + if (detach_cmd == NULL) { + logger("Failed to create detach command"); + } else { + char *detach_response = NULL; + err = debug_proxy_send_command2(debug_proxy, detach_cmd, &detach_response); + debugserver_command_free(detach_cmd); + + if (err) { + logger("Failed to detach from process: %d", err); + } else if (detach_response != NULL) { + logger("Detach response: %s", detach_response); + idevice_string_free(detach_response); + } + } +} + +int debug_app(IdeviceProviderHandle* tcp_provider, const char *bundle_id, LogFuncC logger, DebugAppCallback callback) { // Initialize logger - idevice_init_logger(Debug, Disabled, NULL); - IdeviceErrorCode err = IdeviceSuccess; + idevice_init_logger(Info, Disabled, NULL); + IdeviceFfiError* err = 0; - // Connect to CoreDeviceProxy CoreDeviceProxyHandle *core_device = NULL; - err = core_device_proxy_connect_tcp(tcp_provider, &core_device); - if (err != IdeviceSuccess) { - logger("Failed to connect to CoreDeviceProxy: %d", err); - return 1; + err = core_device_proxy_connect(tcp_provider, &core_device); + if (err != NULL) { + fprintf(stderr, "Failed to connect to CoreDeviceProxy: [%d] %s\n", + err->code, err->message); + idevice_error_free(err); + return 1; } - - // Get server RSD port + uint16_t rsd_port; err = core_device_proxy_get_server_rsd_port(core_device, &rsd_port); - if (err != IdeviceSuccess) { - logger("Failed to get server RSD port: %d", err); - core_device_proxy_free(core_device); - return 1; - } - logger("Server RSD Port: %d", rsd_port); - - /***************************************************************** - * Create TCP Tunnel Adapter - *****************************************************************/ - logger("=== Creating TCP Tunnel Adapter ==="); - + if (err != NULL) { + fprintf(stderr, "Failed to get server RSD port: [%d] %s\n", err->code, + err->message); + idevice_error_free(err); + core_device_proxy_free(core_device); + return 1; + } + printf("Server RSD Port: %d\n", rsd_port); + + printf("\n=== Creating TCP Tunnel Adapter ===\n"); + AdapterHandle *adapter = NULL; err = core_device_proxy_create_tcp_adapter(core_device, &adapter); - if (err != IdeviceSuccess) { - logger("Failed to create TCP adapter: %d", err); - core_device_proxy_free(core_device); - return 1; - } - - // Connect to RSD port - err = adapter_connect(adapter, rsd_port); - if (err != IdeviceSuccess) { - logger("Failed to connect to RSD port: %d", err); - adapter_free(adapter); - return 1; + if (err != NULL) { + fprintf(stderr, "Failed to create TCP adapter: [%d] %s\n", err->code, + err->message); + idevice_error_free(err); + return 1; } - logger("Successfully connected to RSD port"); - - /***************************************************************** - * XPC Device Setup - *****************************************************************/ - logger("=== Setting up XPC Device ==="); - - XPCDeviceAdapterHandle *xpc_device = NULL; - err = xpc_device_new(adapter, &xpc_device); - if (err != IdeviceSuccess) { - logger("Failed to create XPC device: %d", err); - adapter_free(adapter); - return 1; - } - - // Get DebugProxy service - XPCServiceHandle *debug_service = NULL; - err = xpc_device_get_service(xpc_device, "com.apple.internal.dt.remote.debugproxy", &debug_service); - if (err != IdeviceSuccess) { - logger("Failed to get debug proxy service: %d", err); - xpc_device_free(xpc_device); - return 1; - } - - // Get ProcessControl service - XPCServiceHandle *pc_service = NULL; - err = xpc_device_get_service(xpc_device, "com.apple.instruments.dtservicehub", &pc_service); - if (err != IdeviceSuccess) { - logger("Failed to get process control service: %d", err); - xpc_device_free(xpc_device); - return 1; - } - - /***************************************************************** - * Process Control - Launch App - *****************************************************************/ - logger("=== Launching App ==="); - uint64_t pid; - - // Get the adapter back from the XPC device - AdapterHandle *pc_adapter = NULL; - err = xpc_device_adapter_into_inner(xpc_device, &pc_adapter); - if (err != IdeviceSuccess) { - logger("Failed to extract adapter: %d", err); - xpc_device_free(xpc_device); - return 1; - } - - // Connect to process control port - err = adapter_connect(pc_adapter, pc_service->port); - if (err != IdeviceSuccess) { - logger("Failed to connect to process control port: %d", err); - adapter_free(pc_adapter); - xpc_service_free(pc_service); - xpc_service_free(debug_service); - return 1; + AdapterStreamHandle *stream = NULL; + err = adapter_connect(adapter, rsd_port, (ReadWriteOpaque **)&stream); + if (err != NULL) { + fprintf(stderr, "Failed to connect to RSD port: [%d] %s\n", err->code, + err->message); + idevice_error_free(err); + adapter_free(adapter); + return 1; + } + printf("Successfully connected to RSD port\n"); + + printf("\n=== Performing RSD Handshake ===\n"); + + RsdHandshakeHandle *handshake = NULL; + err = rsd_handshake_new((ReadWriteOpaque *)stream, &handshake); + if (err != NULL) { + fprintf(stderr, "Failed to perform RSD handshake: [%d] %s\n", err->code, + err->message); + idevice_error_free(err); + adapter_close(stream); + adapter_free(adapter); + return 1; } - logger("Successfully connected to process control port"); // Create RemoteServerClient - RemoteServerAdapterHandle *remote_server = NULL; - err = remote_server_adapter_new(pc_adapter, &remote_server); - if (err != IdeviceSuccess) { - logger("Failed to create remote server: %d", err); - adapter_free(pc_adapter); - xpc_service_free(pc_service); - xpc_service_free(debug_service); - return 1; + RemoteServerHandle *remote_server = NULL; + err = remote_server_connect_rsd(adapter, handshake, &remote_server); + if (err != NULL) { + fprintf(stderr, "Failed to create remote server: [%d] %s", err->code, + err->message); + idevice_error_free(err); + adapter_free(adapter); + rsd_handshake_free(handshake); + return 1; } - + + printf("\n=== Testing Process Control ===\n"); + // Create ProcessControlClient - ProcessControlAdapterHandle *process_control = NULL; + ProcessControlHandle *process_control = NULL; err = process_control_new(remote_server, &process_control); - if (err != IdeviceSuccess) { - logger("Failed to create process control client: %d", err); - remote_server_free(remote_server); - xpc_service_free(pc_service); - xpc_service_free(debug_service); - return 1; + if (err != NULL) { + fprintf(stderr, "Failed to create process control client: [%d] %s", + err->code, err->message); + idevice_error_free(err); + remote_server_free(remote_server); + return 1; } - // Launch application - + uint64_t pid; err = process_control_launch_app(process_control, bundle_id, NULL, 0, NULL, 0, true, false, &pid); - if (err != IdeviceSuccess) { - logger("Failed to launch app: %d", err); - process_control_free(process_control); - remote_server_free(remote_server); - xpc_service_free(pc_service); - xpc_service_free(debug_service); - return 1; - } - - logger("Successfully launched app with PID: %" PRIu64 "", pid); - - // Disable memory limit for PID - err = process_control_disable_memory_limit(process_control, pid); - if (err != IdeviceSuccess) { - logger("failed to disable memory limit: %d", err); - } - - /***************************************************************** - * Debug Proxy - Attach to Process - *****************************************************************/ - logger("=== Attaching Debugger ==="); - - // Get the adapter back from the remote server - AdapterHandle *debug_adapter = NULL; - err = remote_server_adapter_into_inner(remote_server, &debug_adapter); - if (err != IdeviceSuccess) { - logger("Failed to extract adapter: %d", err); - xpc_service_free(debug_service); - process_control_free(process_control); - remote_server_free(remote_server); - xpc_service_free(pc_service); - return 1; - } - - // Connect to debug proxy port - err = adapter_connect(debug_adapter, debug_service->port); - if (err != IdeviceSuccess) { - logger("Failed to connect to debug proxy port: %d", err); - adapter_free(debug_adapter); - xpc_service_free(debug_service); - process_control_free(process_control); - remote_server_free(remote_server); - xpc_service_free(pc_service); - return 1; - } - logger("Successfully connected to debug proxy port"); - - // Create DebugProxyClient - DebugProxyAdapterHandle *debug_proxy = NULL; - err = debug_proxy_adapter_new(debug_adapter, &debug_proxy); - if (err != IdeviceSuccess) { - logger("Failed to create debug proxy client: %d", err); - adapter_free(debug_adapter); - xpc_service_free(debug_service); - process_control_free(process_control); - remote_server_free(remote_server); - xpc_service_free(pc_service); - return 1; - } - - // Send vAttach command with PID in hex - char attach_command[64]; - snprintf(attach_command, sizeof(attach_command), "vAttach;%" PRIx64, pid); - - DebugserverCommandHandle *attach_cmd = debugserver_command_new(attach_command, NULL, 0); - if (attach_cmd == NULL) { - logger("Failed to create attach command"); - debug_proxy_free(debug_proxy); - xpc_service_free(debug_service); - process_control_free(process_control); - remote_server_free(remote_server); - xpc_service_free(pc_service); - return 1; - } - - char *attach_response = NULL; - err = debug_proxy_send_command(debug_proxy, attach_cmd, &attach_response); - debugserver_command_free(attach_cmd); - - if (err != IdeviceSuccess) { - logger("Failed to attach to process: %d", err); - } else if (attach_response != NULL) { - logger("Attach response: %s", attach_response); - idevice_string_free(attach_response); + if (err != NULL) { + fprintf(stderr, "Failed to launch app: [%d] %s", err->code, err->message); + idevice_error_free(err); + process_control_free(process_control); + remote_server_free(remote_server); + return 1; + } + printf("Successfully launched app with PID: %llu\n", pid); + + printf("\n=== Setting up Debug Proxy ===\n"); + + DebugProxyHandle *debug_proxy = NULL; + err = debug_proxy_connect_rsd(adapter, handshake, &debug_proxy); + if (err != NULL) { + fprintf(stderr, "Failed to create debug proxy client: [%d] %s\n", err->code, + err->message); + idevice_error_free(err); + rsd_handshake_free(handshake); + adapter_free(adapter); + return 1; } - // Send detach command - DebugserverCommandHandle *detach_cmd = debugserver_command_new("D", NULL, 0); - if (detach_cmd == NULL) { - logger("Failed to create detach command"); - } else { - char *detach_response = NULL; - err = debug_proxy_send_command(debug_proxy, detach_cmd, &detach_response); - err = debug_proxy_send_command(debug_proxy, detach_cmd, &detach_response); - err = debug_proxy_send_command(debug_proxy, detach_cmd, &detach_response); - debugserver_command_free(detach_cmd); - - if (err != IdeviceSuccess) { - logger("Failed to detach from process: %d", err); - } else if (detach_response != NULL) { - logger("Detach response: %s", detach_response); - idevice_string_free(detach_response); - } - } + runDebugServerCommand((int)pid, debug_proxy, logger, callback); /***************************************************************** * Cleanup *****************************************************************/ debug_proxy_free(debug_proxy); - xpc_service_free(debug_service); - xpc_service_free(pc_service); + process_control_free(process_control); + remote_server_free(remote_server); + rsd_handshake_free(handshake); + adapter_free(adapter); logger("Debug session completed"); return 0; } -int debug_app_pid(TcpProviderHandle* tcp_provider, int pid, LogFuncC logger) { - // Connect to CoreDeviceProxy +int debug_app_pid(IdeviceProviderHandle* tcp_provider, int pid, LogFuncC logger, DebugAppCallback callback) { + IdeviceFfiError* err = 0; + CoreDeviceProxyHandle *core_device = NULL; - IdeviceErrorCode err = core_device_proxy_connect_tcp(tcp_provider, &core_device); - if (err != IdeviceSuccess) { - logger("Failed to connect to CoreDeviceProxy: %d", err); - return 1; + err = core_device_proxy_connect(tcp_provider, &core_device); + if (err != NULL) { + fprintf(stderr, "Failed to connect to CoreDeviceProxy: [%d] %s\n", + err->code, err->message); + idevice_error_free(err); + return 1; } - - // Get server RSD port + uint16_t rsd_port; err = core_device_proxy_get_server_rsd_port(core_device, &rsd_port); - if (err != IdeviceSuccess) { - logger("Failed to get server RSD port: %d", err); - core_device_proxy_free(core_device); - return 1; - } - logger("Server RSD Port: %d", rsd_port); - - /***************************************************************** - * Create TCP Tunnel Adapter - *****************************************************************/ - logger("=== Creating TCP Tunnel Adapter ==="); - + if (err != NULL) { + fprintf(stderr, "Failed to get server RSD port: [%d] %s\n", err->code, + err->message); + idevice_error_free(err); + core_device_proxy_free(core_device); + return 1; + } + printf("Server RSD Port: %d\n", rsd_port); + + printf("\n=== Creating TCP Tunnel Adapter ===\n"); + AdapterHandle *adapter = NULL; err = core_device_proxy_create_tcp_adapter(core_device, &adapter); - if (err != IdeviceSuccess) { - logger("Failed to create TCP adapter: %d", err); - core_device_proxy_free(core_device); - return 1; - } - - // Connect to RSD port - err = adapter_connect(adapter, rsd_port); - if (err != IdeviceSuccess) { - logger("Failed to connect to RSD port: %d", err); - adapter_free(adapter); - return 1; + if (err != NULL) { + fprintf(stderr, "Failed to create TCP adapter: [%d] %s\n", err->code, + err->message); + idevice_error_free(err); + return 1; } - logger("Successfully connected to RSD port"); - - /***************************************************************** - * XPC Device Setup - *****************************************************************/ - logger("=== Setting up XPC Device ==="); - - XPCDeviceAdapterHandle *xpc_device = NULL; - err = xpc_device_new(adapter, &xpc_device); - if (err != IdeviceSuccess) { - logger("Failed to create XPC device: %d", err); - adapter_free(adapter); - return 1; - } - - // Get DebugProxy service - XPCServiceHandle *debug_service = NULL; - err = xpc_device_get_service(xpc_device, "com.apple.internal.dt.remote.debugproxy", &debug_service); - if (err != IdeviceSuccess) { - logger("Failed to get debug proxy service: %d", err); - xpc_device_free(xpc_device); - return 1; - } - - // Get ProcessControl service - XPCServiceHandle *pc_service = NULL; - err = xpc_device_get_service(xpc_device, "com.apple.instruments.dtservicehub", &pc_service); - if (err != IdeviceSuccess) { - logger("Failed to get process control service: %d", err); - xpc_device_free(xpc_device); - return 1; - } - - /***************************************************************** - * Process Control - Launch App - *****************************************************************/ - logger("=== Launching App ==="); - // Get the adapter back from the XPC device + AdapterStreamHandle *stream = NULL; + err = adapter_connect(adapter, rsd_port, (ReadWriteOpaque **)&stream); + if (err != NULL) { + fprintf(stderr, "Failed to connect to RSD port: [%d] %s\n", err->code, + err->message); + idevice_error_free(err); + adapter_free(adapter); + return 1; + } + printf("Successfully connected to RSD port\n"); - AdapterHandle *pc_adapter = NULL; - err = xpc_device_adapter_into_inner(xpc_device, &pc_adapter); - if (err != IdeviceSuccess) { - logger("Failed to extract adapter: %d", err); - xpc_device_free(xpc_device); - return 1; - } - - // Connect to process control port - err = adapter_connect(pc_adapter, pc_service->port); - if (err != IdeviceSuccess) { - logger("Failed to connect to process control port: %d", err); - adapter_free(pc_adapter); - xpc_service_free(pc_service); - xpc_service_free(debug_service); - return 1; + printf("\n=== Performing RSD Handshake ===\n"); + + RsdHandshakeHandle *handshake = NULL; + err = rsd_handshake_new((ReadWriteOpaque *)stream, &handshake); + if (err != NULL) { + fprintf(stderr, "Failed to perform RSD handshake: [%d] %s\n", err->code, + err->message); + idevice_error_free(err); + adapter_close(stream); + adapter_free(adapter); + return 1; } - logger("Successfully connected to process control port"); // Create RemoteServerClient - RemoteServerAdapterHandle *remote_server = NULL; - err = remote_server_adapter_new(pc_adapter, &remote_server); - if (err != IdeviceSuccess) { - logger("Failed to create remote server: %d", err); - adapter_free(pc_adapter); - xpc_service_free(pc_service); - xpc_service_free(debug_service); - return 1; - } - - // Create ProcessControlClient - ProcessControlAdapterHandle *process_control = NULL; - err = process_control_new(remote_server, &process_control); - if (err != IdeviceSuccess) { - logger("Failed to create process control client: %d", err); - remote_server_free(remote_server); - xpc_service_free(pc_service); - xpc_service_free(debug_service); - return 1; - } - - // Disable memory limit for PID - err = process_control_disable_memory_limit(process_control, pid); - if (err != IdeviceSuccess) { - logger("failed to disable memory limit: %d", err); + RemoteServerHandle *remote_server = NULL; + err = remote_server_connect_rsd(adapter, handshake, &remote_server); + if (err != NULL) { + fprintf(stderr, "Failed to create remote server: [%d] %s", err->code, + err->message); + idevice_error_free(err); + adapter_free(adapter); + rsd_handshake_free(handshake); + return 1; } - - /***************************************************************** - * Debug Proxy - Attach to Process - *****************************************************************/ - logger("=== Attaching Debugger ==="); - - // Get the adapter back from the remote server - AdapterHandle *debug_adapter = NULL; - err = remote_server_adapter_into_inner(remote_server, &debug_adapter); - if (err != IdeviceSuccess) { - logger("Failed to extract adapter: %d", err); - xpc_service_free(debug_service); - process_control_free(process_control); - remote_server_free(remote_server); - xpc_service_free(pc_service); - return 1; - } - - // Connect to debug proxy port - err = adapter_connect(debug_adapter, debug_service->port); - if (err != IdeviceSuccess) { - logger("Failed to connect to debug proxy port: %d", err); - adapter_free(debug_adapter); - xpc_service_free(debug_service); - process_control_free(process_control); - remote_server_free(remote_server); - xpc_service_free(pc_service); - return 1; - } - logger("Successfully connected to debug proxy port"); - - // Create DebugProxyClient - DebugProxyAdapterHandle *debug_proxy = NULL; - err = debug_proxy_adapter_new(debug_adapter, &debug_proxy); - if (err != IdeviceSuccess) { - logger("Failed to create debug proxy client: %d", err); - adapter_free(debug_adapter); - xpc_service_free(debug_service); - process_control_free(process_control); - remote_server_free(remote_server); - xpc_service_free(pc_service); - return 1; - } - - // Send vAttach command with PID in hex - char attach_command[64]; - snprintf(attach_command, sizeof(attach_command), "vAttach;%" PRIx64, pid); - - DebugserverCommandHandle *attach_cmd = debugserver_command_new(attach_command, NULL, 0); - if (attach_cmd == NULL) { - logger("Failed to create attach command"); - debug_proxy_free(debug_proxy); - xpc_service_free(debug_service); - process_control_free(process_control); - remote_server_free(remote_server); - xpc_service_free(pc_service); - return 1; + + printf("\n=== Setting up Debug Proxy ===\n"); + + DebugProxyHandle *debug_proxy = NULL; + err = debug_proxy_connect_rsd(adapter, handshake, &debug_proxy); + if (err != NULL) { + fprintf(stderr, "Failed to create debug proxy client: [%d] %s\n", err->code, + err->message); + idevice_error_free(err); + rsd_handshake_free(handshake); + adapter_free(adapter); + return 1; } - char *attach_response = NULL; - err = debug_proxy_send_command(debug_proxy, attach_cmd, &attach_response); - debugserver_command_free(attach_cmd); - - if (err != IdeviceSuccess) { - logger("Failed to attach to process: %d", err); - } else if (attach_response != NULL) { - logger("Attach response: %s", attach_response); - idevice_string_free(attach_response); - } - // Send detach command - DebugserverCommandHandle *detach_cmd = debugserver_command_new("D", NULL, 0); - if (detach_cmd == NULL) { - logger("Failed to create detach command"); - } else { - char *detach_response = NULL; - err = debug_proxy_send_command(debug_proxy, detach_cmd, &detach_response); - err = debug_proxy_send_command(debug_proxy, detach_cmd, &detach_response); - err = debug_proxy_send_command(debug_proxy, detach_cmd, &detach_response); - debugserver_command_free(detach_cmd); - - if (err != IdeviceSuccess) { - logger("Failed to detach from process: %d", err); - } else if (detach_response != NULL) { - logger("Detach response: %s", detach_response); - idevice_string_free(detach_response); - } - } + runDebugServerCommand(pid, debug_proxy, logger, callback); /***************************************************************** * Cleanup *****************************************************************/ debug_proxy_free(debug_proxy); - xpc_service_free(debug_service); - xpc_service_free(pc_service); + rsd_handshake_free(handshake); + adapter_free(adapter); logger("Debug session completed"); return 0; diff --git a/StikJIT/idevice/jit.h b/StikJIT/idevice/jit.h index eb12442c..88be3ff9 100644 --- a/StikJIT/idevice/jit.h +++ b/StikJIT/idevice/jit.h @@ -11,7 +11,9 @@ #include "idevice.h" typedef void (^LogFuncC)(const char* message, ...); -int debug_app(TcpProviderHandle* provider, const char *bundle_id, LogFuncC logger); -int debug_app_pid(TcpProviderHandle* provider, int pid, LogFuncC logger); +typedef void (^DebugAppCallback)(int pid, struct DebugProxyHandle* debug_proxy, dispatch_semaphore_t semaphore); +int debug_app(IdeviceProviderHandle* tcp_provider, const char *bundle_id, LogFuncC logger, DebugAppCallback callback); +IdeviceFfiError* debug_proxy_send_command2(struct DebugProxyHandle *handle, struct DebugserverCommandHandle *command, char **response); +int debug_app_pid(IdeviceProviderHandle* tcp_provider, int pid, LogFuncC logger, DebugAppCallback callback); #endif /* JIT_H */ diff --git a/StikJIT/idevice/libem_proxy.a b/StikJIT/idevice/libem_proxy.a deleted file mode 100644 index 934bdf50..00000000 Binary files a/StikJIT/idevice/libem_proxy.a and /dev/null differ diff --git a/StikJIT/idevice/libidevice_ffi.a b/StikJIT/idevice/libidevice_ffi.a index 594e0ef9..57bc7a31 100644 Binary files a/StikJIT/idevice/libidevice_ffi.a and b/StikJIT/idevice/libidevice_ffi.a differ