From a16405f01190d8135fedbf78001c65244b2b1272 Mon Sep 17 00:00:00 2001 From: Jackson Coxson Date: Thu, 14 Aug 2025 17:02:58 -0600 Subject: [PATCH] Separate headers into cpp source files --- cpp/examples/CMakeLists.txt | 121 +++++---- cpp/examples/idevice_id.cpp | 5 +- cpp/examples/ideviceinfo.cpp | 64 +++++ cpp/include/ffi.hpp | 29 -- cpp/include/idevice++.hpp | 88 ------- cpp/include/idevice++/bindings.hpp | 8 + cpp/include/idevice++/ffi.hpp | 21 ++ cpp/include/idevice++/idevice.hpp | 63 +++++ cpp/include/idevice++/lockdown.hpp | 45 ++++ cpp/include/idevice++/pairing_file.hpp | 44 ++++ cpp/include/idevice++/provider.hpp | 49 ++++ cpp/include/idevice++/usbmuxd.hpp | 127 +++++++++ cpp/include/pairing_file.hpp | 82 ------ cpp/include/usbmuxd.hpp | 351 ------------------------- cpp/src/ffi.cpp | 17 ++ cpp/src/idevice.cpp | 56 ++++ cpp/src/lockdown.cpp | 67 +++++ cpp/src/pairing_file.cpp | 53 ++++ cpp/src/provider.cpp | 52 ++++ cpp/src/usbmuxd.cpp | 161 ++++++++++++ ffi/idevice.hpp | 16 -- ffi/src/lib.rs | 15 ++ ffi/src/lockdown.rs | 2 +- ffi/src/usbmuxd.rs | 20 ++ 24 files changed, 940 insertions(+), 616 deletions(-) create mode 100644 cpp/examples/ideviceinfo.cpp delete mode 100644 cpp/include/ffi.hpp delete mode 100644 cpp/include/idevice++.hpp create mode 100644 cpp/include/idevice++/bindings.hpp create mode 100644 cpp/include/idevice++/ffi.hpp create mode 100644 cpp/include/idevice++/idevice.hpp create mode 100644 cpp/include/idevice++/lockdown.hpp create mode 100644 cpp/include/idevice++/pairing_file.hpp create mode 100644 cpp/include/idevice++/provider.hpp create mode 100644 cpp/include/idevice++/usbmuxd.hpp delete mode 100644 cpp/include/pairing_file.hpp delete mode 100644 cpp/include/usbmuxd.hpp create mode 100644 cpp/src/ffi.cpp create mode 100644 cpp/src/idevice.cpp create mode 100644 cpp/src/lockdown.cpp create mode 100644 cpp/src/pairing_file.cpp create mode 100644 cpp/src/provider.cpp create mode 100644 cpp/src/usbmuxd.cpp delete mode 100644 ffi/idevice.hpp diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt index c04a0eb..7754f81 100644 --- a/cpp/examples/CMakeLists.txt +++ b/cpp/examples/CMakeLists.txt @@ -1,13 +1,17 @@ # Jackson Coxson -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.15) project(IdeviceFFI CXX) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) -# Set the paths -set(HEADER_FILE ${CMAKE_SOURCE_DIR}/../../ffi/idevice.h) +# Paths +set(IDEVICE_CPP_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/../include) # public C++ headers +set(IDEVICE_CPP_SRC_DIR ${CMAKE_SOURCE_DIR}/../src) # C++ .cpp files +set(IDEVICE_FFI_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/../../ffi) # Rust FFI headers (idevice.h) if (MSVC) set(STATIC_LIB ${CMAKE_SOURCE_DIR}/../../target/release/idevice_ffi.lib) else() @@ -16,57 +20,84 @@ endif() set(EXAMPLES_DIR ${CMAKE_SOURCE_DIR}/../examples) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) -set(IDEVICE_CPP_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/../include) # cpp/include -set(IDEVICE_FFI_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/../../ffi) # ffi/ - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) +# Warnings if (MSVC) add_compile_options(/W4 /permissive- /EHsc) else() add_compile_options(-Wall -Wextra -Wpedantic) endif() -# Find all C++ example files +# ---- Build the C++ wrapper library ----------------------------------------- + +# Collect your .cpps (prefer listing explicitly in real projects) +file(GLOB IDEVICE_CPP_SOURCES + ${IDEVICE_CPP_SRC_DIR}/*.cpp +) + +add_library(idevice_cpp STATIC ${IDEVICE_CPP_SOURCES}) + +# Public headers for consumers; FFI headers are needed by your .cpps only +target_include_directories(idevice_cpp + PUBLIC + ${IDEVICE_CPP_INCLUDE_DIR} + PRIVATE + ${IDEVICE_FFI_INCLUDE_DIR} +) + +# Link to the Rust static lib (+ system libs/frameworks). Mark as PUBLIC so dependents inherit. +target_link_libraries(idevice_cpp + PUBLIC + ${STATIC_LIB} +) + +# Unix-y extras frequently required by Rust static libs +if (UNIX AND NOT APPLE) + find_package(Threads REQUIRED) + target_link_libraries(idevice_cpp PUBLIC Threads::Threads dl m) +endif() + +# Apple frameworks (propagate to dependents) +if (APPLE) + target_link_libraries(idevice_cpp PUBLIC + "-framework CoreFoundation" + "-framework Security" + "-framework SystemConfiguration" + "-framework CoreServices" + "-framework IOKit" + "-framework CFNetwork" + ) +endif() + +# Windows system libs often needed with Rust std/tokio/ring +if (WIN32) + target_link_libraries(idevice_cpp PUBLIC + ws2_32 + userenv + ntdll + bcrypt + # iphlpapi # uncomment if needed + # secur32 crypt32 ncrypt # if you switch TLS stacks + ) +endif() + +# Optional: position independent code if you later turn idevice_cpp into a shared lib +set_target_properties(idevice_cpp PROPERTIES POSITION_INDEPENDENT_CODE ON) + +# ---- Build each example and link against idevice_cpp ------------------------ + file(GLOB EXAMPLE_SOURCES ${EXAMPLES_DIR}/*.cpp) -# Create an executable for each example file foreach(EXAMPLE_FILE ${EXAMPLE_SOURCES}) - get_filename_component(EXAMPLE_NAME ${EXAMPLE_FILE} NAME_WE) - add_executable(${EXAMPLE_NAME} ${EXAMPLE_FILE}) + get_filename_component(EXAMPLE_NAME ${EXAMPLE_FILE} NAME_WE) + add_executable(${EXAMPLE_NAME} ${EXAMPLE_FILE}) - target_include_directories(${EXAMPLE_NAME} PRIVATE - ${IDEVICE_CPP_INCLUDE_DIR} - ${IDEVICE_FFI_INCLUDE_DIR} - ) + # Examples include public headers and (if they directly include FFI headers) the FFI dir. + target_include_directories(${EXAMPLE_NAME} PRIVATE + ${IDEVICE_CPP_INCLUDE_DIR} + ${IDEVICE_FFI_INCLUDE_DIR} + ) - # Include the generated header - target_include_directories(${EXAMPLE_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/..) - - # Link the static Rust library - target_link_libraries(${EXAMPLE_NAME} PRIVATE ${STATIC_LIB}) - - # Bulk-link common macOS system frameworks - if(APPLE) - target_link_libraries(${EXAMPLE_NAME} PRIVATE - "-framework CoreFoundation" - "-framework Security" - "-framework SystemConfiguration" - "-framework CoreServices" - "-framework IOKit" - "-framework CFNetwork" - ) - elseif (WIN32) - # System libs required by Rust std/tokio/reqwest/ring on Windows - target_link_libraries(${EXAMPLE_NAME} PRIVATE - ws2_32 # WSAStartup/WSACleanup, closesocket, freeaddrinfo - userenv # GetUserProfileDirectoryW - ntdll # NtReadFile, RtlNtStatusToDosError - bcrypt # ring RNG (BCryptGenRandom) - # iphlpapi # (optional) GetAdaptersAddresses, if you ever see it unresolved - # secur32 crypt32 ncrypt # (only if you switch to schannel/native-tls) - ) - endif() + # Link to your C++ wrapper (inherits Rust lib + frameworks/system libs) + target_link_libraries(${EXAMPLE_NAME} PRIVATE idevice_cpp) endforeach() - diff --git a/cpp/examples/idevice_id.cpp b/cpp/examples/idevice_id.cpp index e2069d0..7ebabf7 100644 --- a/cpp/examples/idevice_id.cpp +++ b/cpp/examples/idevice_id.cpp @@ -1,13 +1,10 @@ // Jackson Coxson -#include "ffi.hpp" -#include "usbmuxd.hpp" +#include #include #include int main() { - std::cout << "Getting devices from usbmuxd\n"; - IdeviceFFI::FfiError e; std::optional u = IdeviceFFI::UsbmuxdConnection::default_new(0, e); diff --git a/cpp/examples/ideviceinfo.cpp b/cpp/examples/ideviceinfo.cpp new file mode 100644 index 0000000..a45968f --- /dev/null +++ b/cpp/examples/ideviceinfo.cpp @@ -0,0 +1,64 @@ +// Jackson Coxson + +#include +#include +#include +#include +#include + +int main() { + idevice_init_logger(Debug, Disabled, NULL); + + IdeviceFFI::FfiError e; + std::optional u = + IdeviceFFI::UsbmuxdConnection::default_new(0, e); + if (!u) { + std::cerr << "failed to connect to usbmuxd"; + std::cerr << e.message; + } + + auto devices = u->get_devices(e); + if (!devices) { + std::cerr << "failed to get devices from usbmuxd"; + std::cerr << e.message; + } + if (devices->empty()) { + std::cerr << "no devices connected"; + std::cerr << e.message; + } + + auto& dev = (*devices)[0]; + + auto udid = dev.get_udid(); + if (!udid) { + std::cerr << "no udid\n"; + return 1; + } + + auto id = dev.get_id(); + if (!id) { + std::cerr << "no id\n"; + return 1; + } + + IdeviceFFI::UsbmuxdAddr addr = IdeviceFFI::UsbmuxdAddr::default_new(); + auto prov = + IdeviceFFI::Provider::usbmuxd_new(std::move(addr), /*tag*/ 0, *udid, *id, "reeeeeeeee", e); + if (!prov) { + std::cerr << "provider failed: " << e.message << "\n"; + return 1; + } + + auto client = IdeviceFFI::Lockdown::connect(*prov, e); + if (!client) { + std::cerr << "lockdown connect failed: " << e.message << "\n"; + return 1; + } + + auto values = client->get_value("", "", e); + if (!values) { + std::cerr << "get values failed: " << e.message << "\n"; + return 1; + } + plist_free(*values); +} diff --git a/cpp/include/ffi.hpp b/cpp/include/ffi.hpp deleted file mode 100644 index 439b020..0000000 --- a/cpp/include/ffi.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// Jackson Coxson - -#ifndef IDEVICE_FFI -#define IDEVICE_FFI - -#include "idevice.hpp" -#include - -namespace IdeviceFFI { -struct FfiError { - int32_t code = 0; - std::string message; - - static FfiError from(const IdeviceFfiError* err) { - FfiError out; - if (err) { - out.code = err->code; - out.message = err->message ? err->message : ""; - idevice_error_free(const_cast(err)); - } - return out; - } - - static FfiError success() { return {0, ""}; } - - explicit operator bool() const { return code != 0; } -}; -} // namespace IdeviceFFI -#endif diff --git a/cpp/include/idevice++.hpp b/cpp/include/idevice++.hpp deleted file mode 100644 index b16217e..0000000 --- a/cpp/include/idevice++.hpp +++ /dev/null @@ -1,88 +0,0 @@ -// Jackson Coxson - -#ifndef IDEVICE_CPP -#define IDEVICE_CPP - -#include "ffi.hpp" -#include "pairing_file.hpp" -#include -#include - -#if defined(_WIN32) && !defined(__MINGW32__) -// MSVC doesn't have BSD u_int* types -using u_int8_t = std::uint8_t; -using u_int16_t = std::uint16_t; -using u_int32_t = std::uint32_t; -using u_int64_t = std::uint64_t; -#endif - -namespace IdeviceFFI { - -class Idevice { - public: - static std::optional - create(IdeviceSocketHandle* socket, const std::string& label, FfiError& err) { - IdeviceHandle* handle = nullptr; - IdeviceFfiError* e = idevice_new(socket, label.c_str(), &handle); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - return Idevice(handle); - } - - static std::optional - create_tcp(const sockaddr* addr, socklen_t addr_len, const std::string& label, FfiError& err) { - IdeviceHandle* handle = nullptr; - IdeviceFfiError* e = idevice_new_tcp_socket(addr, addr_len, label.c_str(), &handle); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - return Idevice(handle); - } - - std::optional get_type(FfiError& err) { - char* type_cstr = nullptr; - IdeviceFfiError* e = idevice_get_type(handle_, &type_cstr); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - - std::string type_str(type_cstr); - idevice_string_free(type_cstr); - return type_str; - } - - bool rsd_checkin(FfiError& err) { - IdeviceFfiError* e = idevice_rsd_checkin(handle_); - if (e) { - err = FfiError::from(e); - return false; - } - return true; - } - - bool start_session(PairingFile& pairing_file, FfiError& err) { - IdeviceFfiError* e = idevice_start_session(handle_, pairing_file.raw()); - if (e) { - err = FfiError::from(e); - return false; - } - return true; - } - - ~Idevice() { - if (handle_) - idevice_free(handle_); - } - - explicit Idevice(IdeviceHandle* h) : handle_(h) {} - - private: - IdeviceHandle* handle_; -}; - -} // namespace IdeviceFFI -#endif diff --git a/cpp/include/idevice++/bindings.hpp b/cpp/include/idevice++/bindings.hpp new file mode 100644 index 0000000..3646e86 --- /dev/null +++ b/cpp/include/idevice++/bindings.hpp @@ -0,0 +1,8 @@ +// Jackson Coxson + +#ifndef IDEVICE_BINDINGS_H +#define IDEVICE_BINDINGS_H +extern "C" { +#include // this file is generated by bindgen +} +#endif diff --git a/cpp/include/idevice++/ffi.hpp b/cpp/include/idevice++/ffi.hpp new file mode 100644 index 0000000..75cea52 --- /dev/null +++ b/cpp/include/idevice++/ffi.hpp @@ -0,0 +1,21 @@ +// Jackson Coxson + +#ifndef IDEVICE_FFI +#define IDEVICE_FFI + +#include +#include + +namespace IdeviceFFI { +class FfiError { + public: + int32_t code = 0; + std::string message; + + FfiError(const IdeviceFfiError* err); + FfiError(); + + explicit operator bool() const { return code != 0; } +}; +} // namespace IdeviceFFI +#endif diff --git a/cpp/include/idevice++/idevice.hpp b/cpp/include/idevice++/idevice.hpp new file mode 100644 index 0000000..c5ce89f --- /dev/null +++ b/cpp/include/idevice++/idevice.hpp @@ -0,0 +1,63 @@ +// Jackson Coxson + +#ifndef IDEVICE_CPP +#define IDEVICE_CPP + +#include +#include +#include +#include + +#if defined(_WIN32) && !defined(__MINGW32__) +// MSVC doesn't have BSD u_int* types +using u_int8_t = std::uint8_t; +using u_int16_t = std::uint16_t; +using u_int32_t = std::uint32_t; +using u_int64_t = std::uint64_t; +#endif + +namespace IdeviceFFI { + +// Generic “bind a free function” deleter +template struct FnDeleter { + void operator()(T* p) const noexcept { + if (p) + FreeFn(p); + } +}; + +using IdevicePtr = std::unique_ptr>; + +class Idevice { + public: + static std::optional + create(IdeviceSocketHandle* socket, const std::string& label, FfiError& err); + + static std::optional + create_tcp(const sockaddr* addr, socklen_t addr_len, const std::string& label, FfiError& err); + + // Methods + std::optional get_type(FfiError& err) const; + bool rsd_checkin(FfiError& err); + bool start_session(const PairingFile& pairing_file, FfiError& err); + + // Ownership/RAII + ~Idevice() noexcept = default; + Idevice(Idevice&&) noexcept = default; + Idevice& operator=(Idevice&&) noexcept = default; + Idevice(const Idevice&) = delete; + Idevice& operator=(const Idevice&) = delete; + + static Idevice adopt(IdeviceHandle* h) noexcept { return Idevice(h); } + + // Accessor + IdeviceHandle* raw() const noexcept { return handle_.get(); } + IdeviceHandle* release() noexcept { return handle_.release(); } + + private: + explicit Idevice(IdeviceHandle* h) noexcept : handle_(h) {} + IdevicePtr handle_{}; +}; + +} // namespace IdeviceFFI +#endif diff --git a/cpp/include/idevice++/lockdown.hpp b/cpp/include/idevice++/lockdown.hpp new file mode 100644 index 0000000..9c962ae --- /dev/null +++ b/cpp/include/idevice++/lockdown.hpp @@ -0,0 +1,45 @@ + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace IdeviceFFI { + +using LockdownPtr = + std::unique_ptr>; + +class Lockdown { + public: + // Factory: connect via Provider + static std::optional connect(Provider& provider, FfiError& err); + + // Factory: wrap an existing Idevice socket (consumes it on success) + static std::optional from_socket(Idevice&& socket, FfiError& err); + + // Ops + bool start_session(const PairingFile& pf, FfiError& err); + std::optional> start_service(const std::string& identifier, + FfiError& err); + std::optional get_value(const char* key, const char* domain, FfiError& err); + + // RAII / moves + ~Lockdown() noexcept = default; + Lockdown(Lockdown&&) noexcept = default; + Lockdown& operator=(Lockdown&&) noexcept = default; + Lockdown(const Lockdown&) = delete; + Lockdown& operator=(const Lockdown&) = delete; + + LockdowndClientHandle* raw() const noexcept { return handle_.get(); } + static Lockdown adopt(LockdowndClientHandle* h) noexcept { return Lockdown(h); } + + private: + explicit Lockdown(LockdowndClientHandle* h) noexcept : handle_(h) {} + LockdownPtr handle_{}; +}; + +} // namespace IdeviceFFI diff --git a/cpp/include/idevice++/pairing_file.hpp b/cpp/include/idevice++/pairing_file.hpp new file mode 100644 index 0000000..04fffa6 --- /dev/null +++ b/cpp/include/idevice++/pairing_file.hpp @@ -0,0 +1,44 @@ +// Jackson Coxson + +#ifndef IDEVICE_PAIRING_FILE +#define IDEVICE_PAIRING_FILE + +#pragma once + +#include +#include +#include +#include + +namespace IdeviceFFI { +struct PairingFileDeleter { + void operator()(IdevicePairingFile* p) const noexcept; +}; + +using PairingFilePtr = std::unique_ptr; + +class PairingFile { + public: + static std::optional read(const std::string& path, FfiError& err); + static std::optional from_bytes(const uint8_t* data, size_t size, FfiError& err); + + ~PairingFile() noexcept = default; // unique_ptr handles destruction + + PairingFile(const PairingFile&) = delete; + PairingFile& operator=(const PairingFile&) = delete; + + PairingFile(PairingFile&&) noexcept = default; // move is correct by default + PairingFile& operator=(PairingFile&&) noexcept = default; + + std::optional> serialize(FfiError& err) const; + + explicit PairingFile(IdevicePairingFile* ptr) noexcept : ptr_(ptr) {} + IdevicePairingFile* raw() const noexcept { return ptr_.get(); } + IdevicePairingFile* release() noexcept { return ptr_.release(); } + + private: + PairingFilePtr ptr_{}; // owns the handle +}; + +} // namespace IdeviceFFI +#endif diff --git a/cpp/include/idevice++/provider.hpp b/cpp/include/idevice++/provider.hpp new file mode 100644 index 0000000..72a7175 --- /dev/null +++ b/cpp/include/idevice++/provider.hpp @@ -0,0 +1,49 @@ +// Jackson Coxson + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace IdeviceFFI { + +class FfiError; +class PairingFile; // has: IdevicePairingFile* raw() const; void release_on_success(); +class UsbmuxdAddr; // has: UsbmuxdAddrHandle* raw() const; void release_on_success(); + +using ProviderPtr = + std::unique_ptr>; + +class Provider { + public: + static std::optional tcp_new(const idevice_sockaddr* ip, + PairingFile&& pairing, + const std::string& label, + FfiError& err); + + static std::optional usbmuxd_new(UsbmuxdAddr&& addr, + uint32_t tag, + const std::string& udid, + uint32_t device_id, + const std::string& label, + FfiError& err); + + ~Provider() noexcept = default; + Provider(Provider&&) noexcept = default; + Provider& operator=(Provider&&) noexcept = default; + Provider(const Provider&) = delete; + Provider& operator=(const Provider&) = delete; + + IdeviceProviderHandle* raw() const noexcept { return handle_.get(); } + static Provider adopt(IdeviceProviderHandle* h) noexcept { return Provider(h); } + IdeviceProviderHandle* release() noexcept { return handle_.release(); } + + private: + explicit Provider(IdeviceProviderHandle* h) noexcept : handle_(h) {} + ProviderPtr handle_{}; +}; + +} // namespace IdeviceFFI diff --git a/cpp/include/idevice++/usbmuxd.hpp b/cpp/include/idevice++/usbmuxd.hpp new file mode 100644 index 0000000..91fc7b4 --- /dev/null +++ b/cpp/include/idevice++/usbmuxd.hpp @@ -0,0 +1,127 @@ +// Jackson Coxson + +#ifndef IDEVICE_USBMUXD_HPP +#define IDEVICE_USBMUXD_HPP + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +namespace IdeviceFFI { + +using AddrPtr = + std::unique_ptr>; +using DevicePtr = std::unique_ptr>; +using ConnectionPtr = + std::unique_ptr>; + +class UsbmuxdAddr { + public: + static std::optional + tcp_new(const sockaddr* addr, socklen_t addr_len, FfiError& err); +#if defined(__unix__) || defined(__APPLE__) + static std::optional unix_new(const std::string& path, FfiError& err); +#endif + static UsbmuxdAddr default_new(); + + ~UsbmuxdAddr() noexcept = default; + UsbmuxdAddr(UsbmuxdAddr&&) noexcept = default; + UsbmuxdAddr& operator=(UsbmuxdAddr&&) noexcept = default; + UsbmuxdAddr(const UsbmuxdAddr&) = delete; + UsbmuxdAddr& operator=(const UsbmuxdAddr&) = delete; + + UsbmuxdAddrHandle* raw() const noexcept { return handle_.get(); } + UsbmuxdAddrHandle* release() noexcept { return handle_.release(); } + static UsbmuxdAddr adopt(UsbmuxdAddrHandle* h) noexcept { return UsbmuxdAddr(h); } + + private: + explicit UsbmuxdAddr(UsbmuxdAddrHandle* h) noexcept : handle_(h) {} + AddrPtr handle_{}; +}; + +class UsbmuxdConnectionType { + public: + enum class Value : uint8_t { Usb = 1, Network = 2, Unknown = 3 }; + explicit UsbmuxdConnectionType(uint8_t v) : _value(static_cast(v)) {} + + std::string to_string() const; // body in .cpp + Value value() const noexcept { return _value; } + bool operator==(Value other) const noexcept { return _value == other; } + + private: + Value _value{Value::Unknown}; +}; + +class UsbmuxdDevice { + public: + ~UsbmuxdDevice() noexcept = default; + UsbmuxdDevice(UsbmuxdDevice&&) noexcept = default; + UsbmuxdDevice& operator=(UsbmuxdDevice&&) noexcept = default; + UsbmuxdDevice(const UsbmuxdDevice&) = delete; + UsbmuxdDevice& operator=(const UsbmuxdDevice&) = delete; + + static UsbmuxdDevice adopt(UsbmuxdDeviceHandle* h) noexcept { return UsbmuxdDevice(h); } + + UsbmuxdDeviceHandle* raw() const noexcept { return handle_.get(); } + + std::optional get_udid() const; + std::optional get_id() const; + std::optional get_connection_type() const; + + private: + explicit UsbmuxdDevice(UsbmuxdDeviceHandle* h) noexcept : handle_(h) {} + DevicePtr handle_{}; + + friend class UsbmuxdConnection; +}; + +class PairingFile; + +class UsbmuxdConnection { + public: + static std::optional + tcp_new(const idevice_sockaddr* addr, idevice_socklen_t addr_len, uint32_t tag, FfiError& err); +#if defined(__unix__) || defined(__APPLE__) + static std::optional + unix_new(const std::string& path, uint32_t tag, FfiError& err); +#endif + static std::optional default_new(uint32_t tag, FfiError& err); + + ~UsbmuxdConnection() noexcept = default; + UsbmuxdConnection(UsbmuxdConnection&&) noexcept = default; + UsbmuxdConnection& operator=(UsbmuxdConnection&&) noexcept = default; + UsbmuxdConnection(const UsbmuxdConnection&) = delete; + UsbmuxdConnection& operator=(const UsbmuxdConnection&) = delete; + + std::optional> get_devices(FfiError& err) const; + std::optional get_buid(FfiError& err) const; + std::optional get_pair_record(const std::string& udid, FfiError& err); + + std::optional + connect_to_device(uint32_t device_id, uint16_t port, const std::string& path, FfiError& err) &&; + std::optional + connect_to_device(uint32_t, uint16_t, const std::string&, FfiError&) & = delete; + std::optional + connect_to_device(uint32_t, uint16_t, const std::string&, FfiError&) const& = delete; + + UsbmuxdConnectionHandle* raw() const noexcept { return handle_.get(); } + + private: + explicit UsbmuxdConnection(UsbmuxdConnectionHandle* h) noexcept : handle_(h) {} + ConnectionPtr handle_{}; +}; + +} // namespace IdeviceFFI +#endif diff --git a/cpp/include/pairing_file.hpp b/cpp/include/pairing_file.hpp deleted file mode 100644 index 9edfcf8..0000000 --- a/cpp/include/pairing_file.hpp +++ /dev/null @@ -1,82 +0,0 @@ -// Jackson Coxson - -#ifndef IDEVICE_PAIRING_FILE -#define IDEVICE_PAIRING_FILE - -#pragma once - -#include "ffi.hpp" -#include -#include -#include - -namespace IdeviceFFI { -class PairingFile { - public: - static std::optional read(const std::string& path, FfiError& err) { - IdevicePairingFile* ptr = nullptr; - IdeviceFfiError* e = idevice_pairing_file_read(path.c_str(), &ptr); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - return PairingFile(ptr); - } - - static std::optional from_bytes(const uint8_t* data, size_t size, FfiError& err) { - IdevicePairingFile* raw = nullptr; - IdeviceFfiError* e = idevice_pairing_file_from_bytes(data, size, &raw); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - return PairingFile(raw); - } - - ~PairingFile() { - if (ptr_) { - idevice_pairing_file_free(ptr_); - } - } - - // Deleted copy constructor and assignment — use unique ownersship - PairingFile(const PairingFile&) = delete; - PairingFile& operator=(const PairingFile&) = delete; - - // Move constructor and assignment - PairingFile(PairingFile&& other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; } - - PairingFile& operator=(PairingFile&& other) noexcept { - if (this != &other) { - if (ptr_) { - idevice_pairing_file_free(ptr_); - } - ptr_ = other.ptr_; - other.ptr_ = nullptr; - } - return *this; - } - - std::optional> serialize(FfiError& err) const { - uint8_t* data = nullptr; - size_t size = 0; - IdeviceFfiError* e = idevice_pairing_file_serialize(ptr_, &data, &size); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - - std::vector result(data, data + size); - delete[] data; // NOTE: adjust this if deallocation uses `free` or a custom function - return result; - } - - explicit PairingFile(IdevicePairingFile* ptr) : ptr_(ptr) {} - IdevicePairingFile* raw() const { return ptr_; } - - private: - IdevicePairingFile* ptr_; -}; - -} // namespace IdeviceFFI -#endif diff --git a/cpp/include/usbmuxd.hpp b/cpp/include/usbmuxd.hpp deleted file mode 100644 index fe33aad..0000000 --- a/cpp/include/usbmuxd.hpp +++ /dev/null @@ -1,351 +0,0 @@ -// Jackson Coxson - -#ifndef IDEVICE_USBMUXD_HPP -#define IDEVICE_USBMUXD_HPP - -#include "ffi.hpp" -#include "idevice++.hpp" -#include "pairing_file.hpp" -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#include -#else -#include -#endif - -namespace IdeviceFFI { - -/// @brief A C++ wrapper for a UsbmuxdAddrHandle. -/// @details This class manages the memory of a UsbmuxdAddrHandle pointer. -/// It is non-copyable but is movable. -class UsbmuxdAddr { - public: - /// @brief Creates a new TCP usbmuxd address. - /// @param addr The socket address to connect to. - /// @param addr_len The length of the socket address. - /// @param err An error that will be populated on failure. - /// @return A UsbmuxdAddr on success, std::nullopt on failure. - static std::optional - tcp_new(const sockaddr* addr, socklen_t addr_len, FfiError& err) { - UsbmuxdAddrHandle* handle = nullptr; - IdeviceFfiError* e = idevice_usbmuxd_tcp_addr_new(addr, addr_len, &handle); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - return UsbmuxdAddr(handle); - } - -#if defined(__unix__) || defined(__APPLE__) - /// @brief Creates a new Unix socket usbmuxd address. - /// @param path The path to the unix socket. - /// @param err An error that will be populated on failure. - /// @return A UsbmuxdAddr on success, std::nullopt on failure. - static std::optional unix_new(const std::string& path, FfiError& err) { - UsbmuxdAddrHandle* handle = nullptr; - IdeviceFfiError* e = idevice_usbmuxd_unix_addr_new(path.c_str(), &handle); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - return UsbmuxdAddr(handle); - } -#endif - - ~UsbmuxdAddr() { - if (handle_) { - idevice_usbmuxd_addr_free(handle_); - } - } - - // Delete copy constructor and assignment operator - UsbmuxdAddr(const UsbmuxdAddr&) = delete; - UsbmuxdAddr& operator=(const UsbmuxdAddr&) = delete; - - // Define move constructor and assignment operator - UsbmuxdAddr(UsbmuxdAddr&& other) noexcept : handle_(other.handle_) { other.handle_ = nullptr; } - UsbmuxdAddr& operator=(UsbmuxdAddr&& other) noexcept { - if (this != &other) { - if (handle_) { - idevice_usbmuxd_addr_free(handle_); - } - handle_ = other.handle_; - other.handle_ = nullptr; - } - return *this; - } - - /// @brief Gets the raw handle. - /// @return The raw UsbmuxdAddrHandle pointer. - UsbmuxdAddrHandle* raw() const { return handle_; } - - private: - explicit UsbmuxdAddr(UsbmuxdAddrHandle* handle) : handle_(handle) {} - UsbmuxdAddrHandle* handle_; -}; - -class UsbmuxdConnectionType { - public: - enum Value : uint8_t { Usb = 1, Network = 2, Unknown = 3 }; - explicit UsbmuxdConnectionType(uint8_t v) : _value(static_cast(v)) {} - - std::string to_string() { - switch (_value) { - case UsbmuxdConnectionType::Usb: - return "USB"; - case UsbmuxdConnectionType::Network: - return "Network"; - case UsbmuxdConnectionType::Unknown: - return "Unknown"; - default: - return "UnknownEnumValue"; - } - } - - Value value() const { return _value; } - - bool operator==(Value other) const { return _value == other; } - - private: - Value _value; -}; - -class UsbmuxdDevice { - public: - ~UsbmuxdDevice() { - if (handle_) { - idevice_usbmuxd_device_free(handle_); - } - } - // Delete copy constructor and assignment operator - UsbmuxdDevice(const UsbmuxdDevice&) = delete; - UsbmuxdDevice& operator=(const UsbmuxdDevice&) = delete; - - // Define move constructor and assignment operator - UsbmuxdDevice(UsbmuxdDevice&& other) noexcept : handle_(other.handle_) { - other.handle_ = nullptr; - } - UsbmuxdDevice& operator=(UsbmuxdDevice&& other) noexcept { - if (this != &other) { - if (handle_) { - idevice_usbmuxd_device_free(handle_); - } - handle_ = other.handle_; - other.handle_ = nullptr; - } - return *this; - } - - /// @brief Gets the raw handle. - /// @return The raw UsbmuxdConnectionHandle pointer. - UsbmuxdDeviceHandle* raw() const { return handle_; } - - std::optional get_udid() { - char* udid = idevice_usbmuxd_device_get_udid(handle_); - if (udid) { - std::string cppUdid(udid); - idevice_string_free(udid); - return cppUdid; - } else { - return std::nullopt; - } - } - - std::optional get_id() { - uint32_t id = idevice_usbmuxd_device_get_device_id(handle_); - if (id == 0) { - return std::nullopt; - } else { - return id; - } - } - - std::optional get_connection_type() { - u_int8_t t = idevice_usbmuxd_device_get_connection_type(handle_); - if (t == 0) { - return std::nullopt; - } else { - } - return static_cast(t); - } - explicit UsbmuxdDevice(UsbmuxdDeviceHandle* handle) : handle_(handle) {} - - private: - UsbmuxdDeviceHandle* handle_; -}; - -/// @brief A C++ wrapper for a UsbmuxdConnectionHandle. -/// @details This class manages the memory of a UsbmuxdConnectionHandle pointer. -/// It is non-copyable but is movable. -class UsbmuxdConnection { - public: - /// @brief Creates a new TCP usbmuxd connection. - /// @param addr The socket address to connect to. - /// @param addr_len The length of the socket address. - /// @param tag A tag that will be returned by usbmuxd responses. - /// @param err An error that will be populated on failure. - /// @return A UsbmuxdConnection on success, std::nullopt on failure. - static std::optional - tcp_new(const idevice_sockaddr* addr, idevice_socklen_t addr_len, uint32_t tag, FfiError& err) { - UsbmuxdConnectionHandle* handle = nullptr; - IdeviceFfiError* e = idevice_usbmuxd_new_tcp_connection(addr, addr_len, tag, &handle); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - return UsbmuxdConnection(handle); - } - -#if defined(__unix__) || defined(__APPLE__) - /// @brief Creates a new Unix socket usbmuxd connection. - /// @param path The path to the unix socket. - /// @param tag A tag that will be returned by usbmuxd responses. - /// @param err An error that will be populated on failure. - /// @return A UsbmuxdConnection on success, std::nullopt on failure. - static std::optional - unix_new(const std::string& path, uint32_t tag, FfiError& err) { - UsbmuxdConnectionHandle* handle = nullptr; - IdeviceFfiError* e = idevice_usbmuxd_new_unix_socket_connection(path.c_str(), tag, &handle); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - return UsbmuxdConnection(handle); - } -#endif - - /// @brief Creates a new usbmuxd connection using the default path. - /// @param tag A tag that will be returned by usbmuxd responses. - /// @param err An error that will be populated on failure. - /// @return A UsbmuxdConnection on success, std::nullopt on failure. - static std::optional default_new(uint32_t tag, FfiError& err) { - UsbmuxdConnectionHandle* handle = nullptr; - IdeviceFfiError* e = idevice_usbmuxd_new_default_connection(tag, &handle); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - return UsbmuxdConnection(handle); - } - - /// @brief Gets a list of all connected devices. - /// @param err An error that will be populated on failure. - /// @return A vector of UsbmuxdDevice objects on success, std::nullopt on failure. - std::optional> get_devices(FfiError& err) { - UsbmuxdDeviceHandle** devices = nullptr; - int count = 0; - IdeviceFfiError* e = idevice_usbmuxd_get_devices(handle_, &devices, &count); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - - std::vector result; - result.reserve(count); - for (int i = 0; i < count; ++i) { - result.emplace_back(devices[i]); - } - - return result; - } - - /// @brief Gets the BUID from the daemon. - /// @param err An error that will be populated on failure. - /// @return The BUID string on success, std::nullopt on failure. - std::optional get_buid(FfiError& err) { - char* buid_c = nullptr; - IdeviceFfiError* e = idevice_usbmuxd_get_buid(handle_, &buid_c); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - std::string buid(buid_c); - idevice_string_free(buid_c); - return buid; - } - - /// @brief Gets the pairing record for a device. - /// @param udid The UDID of the target device. - /// @param err An error that will be populated on failure. - /// @return A PairingFile object on success, std::nullopt on failure. - std::optional get_pair_record(const std::string& udid, FfiError& err) { - IdevicePairingFile* pf_handle = nullptr; - IdeviceFfiError* e = idevice_usbmuxd_get_pair_record(handle_, udid.c_str(), &pf_handle); - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - return PairingFile(pf_handle); - } - - /// @brief Connects to a port on a given device. - /// @details This operation consumes the UsbmuxdConnection object. After a successful call, - /// this object will be invalid and should not be used. - /// @param device_id The ID of the target device. - /// @param port The port number to connect to. - /// @param err An error that will be populated on failure. - /// @return An Idevice connection object on success, std::nullopt on failure. - std::optional - connect_to_device(uint32_t device_id, uint16_t port, const std::string& path, FfiError& err) { - if (!handle_) { - // Can't connect with an invalid handle - return std::nullopt; - } - - IdeviceHandle* idevice_handle = nullptr; - IdeviceFfiError* e = idevice_usbmuxd_connect_to_device( - handle_, device_id, port, path.c_str(), &idevice_handle); - - // The handle is always consumed by the FFI call, so we must invalidate it. - handle_ = nullptr; - - if (e) { - err = FfiError::from(e); - return std::nullopt; - } - - return Idevice(idevice_handle); - } - - ~UsbmuxdConnection() { - if (handle_) { - idevice_usbmuxd_connection_free(handle_); - } - } - - // Delete copy constructor and assignment operator - UsbmuxdConnection(const UsbmuxdConnection&) = delete; - UsbmuxdConnection& operator=(const UsbmuxdConnection&) = delete; - - // Define move constructor and assignment operator - UsbmuxdConnection(UsbmuxdConnection&& other) noexcept : handle_(other.handle_) { - other.handle_ = nullptr; - } - UsbmuxdConnection& operator=(UsbmuxdConnection&& other) noexcept { - if (this != &other) { - if (handle_) { - idevice_usbmuxd_connection_free(handle_); - } - handle_ = other.handle_; - other.handle_ = nullptr; - } - return *this; - } - - /// @brief Gets the raw handle. - /// @return The raw UsbmuxdConnectionHandle pointer. - UsbmuxdConnectionHandle* raw() const { return handle_; } - - private: - explicit UsbmuxdConnection(UsbmuxdConnectionHandle* handle) : handle_(handle) {} - UsbmuxdConnectionHandle* handle_; -}; - -} // namespace IdeviceFFI - -#endif diff --git a/cpp/src/ffi.cpp b/cpp/src/ffi.cpp new file mode 100644 index 0000000..fc95293 --- /dev/null +++ b/cpp/src/ffi.cpp @@ -0,0 +1,17 @@ +// Jackson Coxson + +#include +#include +#include + +namespace IdeviceFFI { +FfiError::FfiError(const IdeviceFfiError* err) + : code(err ? err->code : 0), message(err && err->message ? err->message : "") { + if (err) { + idevice_error_free(const_cast(err)); + } +} + +FfiError::FfiError() : code(0), message("") { +} +} // namespace IdeviceFFI diff --git a/cpp/src/idevice.cpp b/cpp/src/idevice.cpp new file mode 100644 index 0000000..6899092 --- /dev/null +++ b/cpp/src/idevice.cpp @@ -0,0 +1,56 @@ +// Jackson Coxson + +#include + +namespace IdeviceFFI { + +std::optional +Idevice::create(IdeviceSocketHandle* socket, const std::string& label, FfiError& err) { + IdeviceHandle* h = nullptr; + if (IdeviceFfiError* e = idevice_new(socket, label.c_str(), &h)) { + err = FfiError(e); + return std::nullopt; + } + return Idevice(h); +} + +std::optional Idevice::create_tcp(const sockaddr* addr, + socklen_t addr_len, + const std::string& label, + FfiError& err) { + IdeviceHandle* h = nullptr; + if (IdeviceFfiError* e = idevice_new_tcp_socket(addr, addr_len, label.c_str(), &h)) { + err = FfiError(e); + return std::nullopt; + } + return Idevice(h); +} + +std::optional Idevice::get_type(FfiError& err) const { + char* cstr = nullptr; + if (IdeviceFfiError* e = idevice_get_type(handle_.get(), &cstr)) { + err = FfiError(e); + return std::nullopt; + } + std::string out(cstr); + idevice_string_free(cstr); + return out; +} + +bool Idevice::rsd_checkin(FfiError& err) { + if (IdeviceFfiError* e = idevice_rsd_checkin(handle_.get())) { + err = FfiError(e); + return false; + } + return true; +} + +bool Idevice::start_session(const PairingFile& pairing_file, FfiError& err) { + if (IdeviceFfiError* e = idevice_start_session(handle_.get(), pairing_file.raw())) { + err = FfiError(e); + return false; + } + return true; +} + +} // namespace IdeviceFFI diff --git a/cpp/src/lockdown.cpp b/cpp/src/lockdown.cpp new file mode 100644 index 0000000..e00659a --- /dev/null +++ b/cpp/src/lockdown.cpp @@ -0,0 +1,67 @@ +// Jackson Coxson + +#include +#include +#include +#include + +namespace IdeviceFFI { + +std::optional Lockdown::connect(Provider& provider, FfiError& err) { + LockdowndClientHandle* out = nullptr; + + if (IdeviceFfiError* e = ::lockdownd_connect(provider.raw(), &out)) { + // Rust freed the provider on error -> abandon our ownership to avoid double free. + // Your Provider wrapper should expose release(). + provider.release(); + err = FfiError(e); + return std::nullopt; + } + // Success: provider is NOT consumed; keep ownership. + return Lockdown::adopt(out); +} + +std::optional Lockdown::from_socket(Idevice&& socket, FfiError& err) { + LockdowndClientHandle* out = nullptr; + + if (IdeviceFfiError* e = ::lockdownd_new(socket.raw(), &out)) { + // Error: Rust did NOT consume the socket (it returns early for invalid args), + // so keep ownership; report error. + err = FfiError(e); + return std::nullopt; + } + // Success: Rust consumed the socket -> abandon our ownership. + socket.release(); + return Lockdown::adopt(out); +} + +bool Lockdown::start_session(const PairingFile& pf, FfiError& err) { + if (IdeviceFfiError* e = ::lockdownd_start_session(handle_.get(), pf.raw())) { + err = FfiError(e); + return false; + } + return true; +} + +std::optional> Lockdown::start_service(const std::string& identifier, + FfiError& err) { + uint16_t port = 0; + bool ssl = false; + if (IdeviceFfiError* e = + ::lockdownd_start_service(handle_.get(), identifier.c_str(), &port, &ssl)) { + err = FfiError(e); + return std::nullopt; + } + return std::make_pair(port, ssl); +} + +std::optional Lockdown::get_value(const char* key, const char* domain, FfiError& err) { + plist_t out = nullptr; + if (IdeviceFfiError* e = ::lockdownd_get_value(handle_.get(), key, domain, &out)) { + err = FfiError(e); + return std::nullopt; + } + return out; // caller now owns `out` and must free with the plist API +} + +} // namespace IdeviceFFI diff --git a/cpp/src/pairing_file.cpp b/cpp/src/pairing_file.cpp new file mode 100644 index 0000000..bc2f388 --- /dev/null +++ b/cpp/src/pairing_file.cpp @@ -0,0 +1,53 @@ +// Jackson Coxson + +#include +#include +#include + +namespace IdeviceFFI { + +// Deleter definition (out-of-line) +void PairingFileDeleter::operator()(IdevicePairingFile* p) const noexcept { + if (p) + idevice_pairing_file_free(p); +} + +// Static member definitions +std::optional PairingFile::read(const std::string& path, FfiError& err) { + IdevicePairingFile* ptr = nullptr; + if (IdeviceFfiError* e = idevice_pairing_file_read(path.c_str(), &ptr)) { + err = FfiError(e); + return std::nullopt; + } + return PairingFile(ptr); +} + +std::optional +PairingFile::from_bytes(const uint8_t* data, size_t size, FfiError& err) { + IdevicePairingFile* raw = nullptr; + if (IdeviceFfiError* e = idevice_pairing_file_from_bytes(data, size, &raw)) { + err = FfiError(e); + return std::nullopt; + } + return PairingFile(raw); +} + +std::optional> PairingFile::serialize(FfiError& err) const { + if (!ptr_) { + return std::nullopt; + } + + uint8_t* data = nullptr; + size_t size = 0; + + if (IdeviceFfiError* e = idevice_pairing_file_serialize(ptr_.get(), &data, &size)) { + err = FfiError(e); + return std::nullopt; + } + + std::vector out(data, data + size); + idevice_data_free(data, size); + return out; +} + +} // namespace IdeviceFFI diff --git a/cpp/src/provider.cpp b/cpp/src/provider.cpp new file mode 100644 index 0000000..80c9c3c --- /dev/null +++ b/cpp/src/provider.cpp @@ -0,0 +1,52 @@ +// Jackson Coxson + +#include +#include +#include + +namespace IdeviceFFI { + +std::optional Provider::tcp_new(const idevice_sockaddr* ip, + PairingFile&& pairing, + const std::string& label, + FfiError& err) { + IdeviceProviderHandle* out = nullptr; + + // Call with exact types; do NOT cast to void* + if (IdeviceFfiError* e = idevice_tcp_provider_new( + ip, static_cast(pairing.raw()), label.c_str(), &out)) { + err = FfiError(e); + return std::nullopt; + } + + // Success: Rust consumed the pairing file -> abandon our ownership + pairing.release(); // implement as: ptr_.release() in PairingFile + + return Provider::adopt(out); +} + +std::optional Provider::usbmuxd_new(UsbmuxdAddr&& addr, + uint32_t tag, + const std::string& udid, + uint32_t device_id, + const std::string& label, + FfiError& err) { + IdeviceProviderHandle* out = nullptr; + + if (IdeviceFfiError* e = usbmuxd_provider_new(static_cast(addr.raw()), + tag, + udid.c_str(), + device_id, + label.c_str(), + &out)) { + err = FfiError(e); + return std::nullopt; + } + + // Success: Rust consumed the addr -> abandon our ownership + addr.release(); // implement as: ptr_.release() in UsbmuxdAddr + + return Provider::adopt(out); +} + +} // namespace IdeviceFFI diff --git a/cpp/src/usbmuxd.cpp b/cpp/src/usbmuxd.cpp new file mode 100644 index 0000000..45bdb08 --- /dev/null +++ b/cpp/src/usbmuxd.cpp @@ -0,0 +1,161 @@ +// Jackson Coxson + +#include +#include + +namespace IdeviceFFI { + +// ---------- UsbmuxdAddr ---------- +std::optional +UsbmuxdAddr::tcp_new(const sockaddr* addr, socklen_t addr_len, FfiError& err) { + UsbmuxdAddrHandle* h = nullptr; + if (IdeviceFfiError* e = idevice_usbmuxd_tcp_addr_new(addr, addr_len, &h)) { + err = FfiError(e); + return std::nullopt; + } + return UsbmuxdAddr(h); +} + +#if defined(__unix__) || defined(__APPLE__) +std::optional UsbmuxdAddr::unix_new(const std::string& path, FfiError& err) { + UsbmuxdAddrHandle* h = nullptr; + if (IdeviceFfiError* e = idevice_usbmuxd_unix_addr_new(path.c_str(), &h)) { + err = FfiError(e); + return std::nullopt; + } + return UsbmuxdAddr(h); +} +#endif + +UsbmuxdAddr UsbmuxdAddr::default_new() { + UsbmuxdAddrHandle* h = nullptr; + idevice_usbmuxd_default_addr_new(&h); + return UsbmuxdAddr::adopt(h); +} + +// ---------- UsbmuxdConnectionType ---------- +std::string UsbmuxdConnectionType::to_string() const { + switch (_value) { + case Value::Usb: + return "USB"; + case Value::Network: + return "Network"; + case Value::Unknown: + return "Unknown"; + default: + return "UnknownEnumValue"; + } +} + +// ---------- UsbmuxdDevice ---------- +std::optional UsbmuxdDevice::get_udid() const { + char* c = idevice_usbmuxd_device_get_udid(handle_.get()); + if (!c) + return std::nullopt; + std::string out(c); + idevice_string_free(c); + return out; +} + +std::optional UsbmuxdDevice::get_id() const { + uint32_t id = idevice_usbmuxd_device_get_device_id(handle_.get()); + if (id == 0) + return std::nullopt; // adjust if 0 can be valid + return id; +} + +std::optional UsbmuxdDevice::get_connection_type() const { + uint8_t t = idevice_usbmuxd_device_get_connection_type(handle_.get()); + if (t == 0) + return std::nullopt; // adjust to your API contract + return UsbmuxdConnectionType(t); +} + +// ---------- UsbmuxdConnection ---------- +std::optional UsbmuxdConnection::tcp_new(const idevice_sockaddr* addr, + idevice_socklen_t addr_len, + uint32_t tag, + FfiError& err) { + UsbmuxdConnectionHandle* h = nullptr; + if (IdeviceFfiError* e = idevice_usbmuxd_new_tcp_connection(addr, addr_len, tag, &h)) { + err = FfiError(e); + return std::nullopt; + } + return UsbmuxdConnection(h); +} + +#if defined(__unix__) || defined(__APPLE__) +std::optional +UsbmuxdConnection::unix_new(const std::string& path, uint32_t tag, FfiError& err) { + UsbmuxdConnectionHandle* h = nullptr; + if (IdeviceFfiError* e = idevice_usbmuxd_new_unix_socket_connection(path.c_str(), tag, &h)) { + err = FfiError(e); + return std::nullopt; + } + return UsbmuxdConnection(h); +} +#endif + +std::optional UsbmuxdConnection::default_new(uint32_t tag, FfiError& err) { + UsbmuxdConnectionHandle* h = nullptr; + if (IdeviceFfiError* e = idevice_usbmuxd_new_default_connection(tag, &h)) { + err = FfiError(e); + return std::nullopt; + } + return UsbmuxdConnection(h); +} + +std::optional> UsbmuxdConnection::get_devices(FfiError& err) const { + UsbmuxdDeviceHandle** list = nullptr; + int count = 0; + if (IdeviceFfiError* e = idevice_usbmuxd_get_devices(handle_.get(), &list, &count)) { + err = FfiError(e); + return std::nullopt; + } + std::vector out; + out.reserve(count); + for (int i = 0; i < count; ++i) { + out.emplace_back(UsbmuxdDevice::adopt(list[i])); + } + return out; +} + +std::optional UsbmuxdConnection::get_buid(FfiError& err) const { + char* c = nullptr; + if (IdeviceFfiError* e = idevice_usbmuxd_get_buid(handle_.get(), &c)) { + err = FfiError(e); + return std::nullopt; + } + std::string out(c); + idevice_string_free(c); + return out; +} + +std::optional UsbmuxdConnection::get_pair_record(const std::string& udid, + FfiError& err) { + IdevicePairingFile* pf = nullptr; + if (IdeviceFfiError* e = idevice_usbmuxd_get_pair_record(handle_.get(), udid.c_str(), &pf)) { + err = FfiError(e); + return std::nullopt; + } + return PairingFile(pf); +} + +std::optional UsbmuxdConnection::connect_to_device(uint32_t device_id, + uint16_t port, + const std::string& path, + FfiError& err) && { + UsbmuxdConnectionHandle* raw = handle_.release(); + + IdeviceHandle* out = nullptr; + IdeviceFfiError* e = + idevice_usbmuxd_connect_to_device(raw, device_id, port, path.c_str(), &out); + + if (e) { + err = FfiError(e); + return std::nullopt; + } + return Idevice::adopt(out); +} + +} // namespace IdeviceFFI diff --git a/ffi/idevice.hpp b/ffi/idevice.hpp deleted file mode 100644 index 6aac99c..0000000 --- a/ffi/idevice.hpp +++ /dev/null @@ -1,16 +0,0 @@ -// Jackson Coxson - Bindings to idevice - https://github.com/jkcoxson/idevice -// This file only wraps the C bindings for C++, the C bindings still must be -// generated. -// ``cargo build --release`` - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "idevice.h" // this file is generated by bindgen - -#ifdef __cplusplus -} -#endif diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index a7b64fc..9bd1833 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -315,3 +315,18 @@ pub unsafe extern "C" fn idevice_string_free(string: *mut c_char) { let _ = unsafe { CString::from_raw(string) }; } } + +/// Frees data allocated by this library +/// +/// # Arguments +/// * [`data`] - The data to free +/// +/// # Safety +/// `data` must be a valid pointer to data that was allocated by this library, +/// or NULL (in which case this function does nothing) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn idevice_data_free(data: *mut u8, len: usize) { + if !data.is_null() { + let _ = unsafe { std::slice::from_raw_parts(data, len) }; + } +} diff --git a/ffi/src/lockdown.rs b/ffi/src/lockdown.rs index 81b0580..63bf36d 100644 --- a/ffi/src/lockdown.rs +++ b/ffi/src/lockdown.rs @@ -12,7 +12,7 @@ use crate::{ pub struct LockdowndClientHandle(pub LockdownClient); -/// Connects to lockdownd service using TCP provider +/// Connects to lockdownd service using provider /// /// # Arguments /// * [`provider`] - An IdeviceProvider diff --git a/ffi/src/usbmuxd.rs b/ffi/src/usbmuxd.rs index fcccd94..3f0b932 100644 --- a/ffi/src/usbmuxd.rs +++ b/ffi/src/usbmuxd.rs @@ -428,6 +428,26 @@ pub unsafe extern "C" fn idevice_usbmuxd_unix_addr_new( null_mut() } +/// Creates a default UsbmuxdAddr struct for the platform +/// +/// # Arguments +/// * [`usbmuxd_addr`] - On success, will be set to point to a newly allocated UsbmuxdAddr handle +/// +/// # Returns +/// An IdeviceFfiError on error, null on success +/// +/// # Safety +/// `usbmuxd_addr` must be a valid, non-null pointer to a location where the handle will be stored +#[unsafe(no_mangle)] +pub unsafe extern "C" fn idevice_usbmuxd_default_addr_new( + usbmuxd_addr: *mut *mut UsbmuxdAddrHandle, +) -> *mut IdeviceFfiError { + let addr = UsbmuxdAddr::default(); + let boxed = Box::new(UsbmuxdAddrHandle(addr)); + unsafe { *usbmuxd_addr = Box::into_raw(boxed) }; + null_mut() +} + /// Frees a UsbmuxdAddr handle /// /// # Arguments