mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 14:36:16 +01:00
Separate headers into cpp source files
This commit is contained in:
@@ -1,13 +1,17 @@
|
|||||||
# Jackson Coxson
|
# Jackson Coxson
|
||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
cmake_minimum_required(VERSION 3.15)
|
||||||
cmake_minimum_required(VERSION 3.10)
|
|
||||||
project(IdeviceFFI CXX)
|
project(IdeviceFFI CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
||||||
# Set the paths
|
# Paths
|
||||||
set(HEADER_FILE ${CMAKE_SOURCE_DIR}/../../ffi/idevice.h)
|
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)
|
if (MSVC)
|
||||||
set(STATIC_LIB ${CMAKE_SOURCE_DIR}/../../target/release/idevice_ffi.lib)
|
set(STATIC_LIB ${CMAKE_SOURCE_DIR}/../../target/release/idevice_ffi.lib)
|
||||||
else()
|
else()
|
||||||
@@ -16,57 +20,84 @@ endif()
|
|||||||
set(EXAMPLES_DIR ${CMAKE_SOURCE_DIR}/../examples)
|
set(EXAMPLES_DIR ${CMAKE_SOURCE_DIR}/../examples)
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
||||||
set(IDEVICE_CPP_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/../include) # cpp/include
|
# Warnings
|
||||||
set(IDEVICE_FFI_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/../../ffi) # ffi/
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
add_compile_options(/W4 /permissive- /EHsc)
|
add_compile_options(/W4 /permissive- /EHsc)
|
||||||
else()
|
else()
|
||||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||||
endif()
|
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)
|
file(GLOB EXAMPLE_SOURCES ${EXAMPLES_DIR}/*.cpp)
|
||||||
|
|
||||||
# Create an executable for each example file
|
|
||||||
foreach(EXAMPLE_FILE ${EXAMPLE_SOURCES})
|
foreach(EXAMPLE_FILE ${EXAMPLE_SOURCES})
|
||||||
get_filename_component(EXAMPLE_NAME ${EXAMPLE_FILE} NAME_WE)
|
get_filename_component(EXAMPLE_NAME ${EXAMPLE_FILE} NAME_WE)
|
||||||
add_executable(${EXAMPLE_NAME} ${EXAMPLE_FILE})
|
add_executable(${EXAMPLE_NAME} ${EXAMPLE_FILE})
|
||||||
|
|
||||||
target_include_directories(${EXAMPLE_NAME} PRIVATE
|
# Examples include public headers and (if they directly include FFI headers) the FFI dir.
|
||||||
${IDEVICE_CPP_INCLUDE_DIR}
|
target_include_directories(${EXAMPLE_NAME} PRIVATE
|
||||||
${IDEVICE_FFI_INCLUDE_DIR}
|
${IDEVICE_CPP_INCLUDE_DIR}
|
||||||
)
|
${IDEVICE_FFI_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
# Include the generated header
|
# Link to your C++ wrapper (inherits Rust lib + frameworks/system libs)
|
||||||
target_include_directories(${EXAMPLE_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/..)
|
target_link_libraries(${EXAMPLE_NAME} PRIVATE idevice_cpp)
|
||||||
|
|
||||||
# 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()
|
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
// Jackson Coxson
|
// Jackson Coxson
|
||||||
|
|
||||||
#include "ffi.hpp"
|
#include <idevice++/usbmuxd.hpp>
|
||||||
#include "usbmuxd.hpp"
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::cout << "Getting devices from usbmuxd\n";
|
|
||||||
|
|
||||||
IdeviceFFI::FfiError e;
|
IdeviceFFI::FfiError e;
|
||||||
std::optional<IdeviceFFI::UsbmuxdConnection> u =
|
std::optional<IdeviceFFI::UsbmuxdConnection> u =
|
||||||
IdeviceFFI::UsbmuxdConnection::default_new(0, e);
|
IdeviceFFI::UsbmuxdConnection::default_new(0, e);
|
||||||
|
|||||||
64
cpp/examples/ideviceinfo.cpp
Normal file
64
cpp/examples/ideviceinfo.cpp
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#include <idevice++/lockdown.hpp>
|
||||||
|
#include <idevice++/provider.hpp>
|
||||||
|
#include <idevice++/usbmuxd.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
idevice_init_logger(Debug, Disabled, NULL);
|
||||||
|
|
||||||
|
IdeviceFFI::FfiError e;
|
||||||
|
std::optional<IdeviceFFI::UsbmuxdConnection> 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);
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
// Jackson Coxson
|
|
||||||
|
|
||||||
#ifndef IDEVICE_FFI
|
|
||||||
#define IDEVICE_FFI
|
|
||||||
|
|
||||||
#include "idevice.hpp"
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
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<IdeviceFfiError*>(err));
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static FfiError success() { return {0, ""}; }
|
|
||||||
|
|
||||||
explicit operator bool() const { return code != 0; }
|
|
||||||
};
|
|
||||||
} // namespace IdeviceFFI
|
|
||||||
#endif
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
// Jackson Coxson
|
|
||||||
|
|
||||||
#ifndef IDEVICE_CPP
|
|
||||||
#define IDEVICE_CPP
|
|
||||||
|
|
||||||
#include "ffi.hpp"
|
|
||||||
#include "pairing_file.hpp"
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#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<Idevice>
|
|
||||||
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<Idevice>
|
|
||||||
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<std::string> 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
|
|
||||||
8
cpp/include/idevice++/bindings.hpp
Normal file
8
cpp/include/idevice++/bindings.hpp
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#ifndef IDEVICE_BINDINGS_H
|
||||||
|
#define IDEVICE_BINDINGS_H
|
||||||
|
extern "C" {
|
||||||
|
#include <idevice.h> // this file is generated by bindgen
|
||||||
|
}
|
||||||
|
#endif
|
||||||
21
cpp/include/idevice++/ffi.hpp
Normal file
21
cpp/include/idevice++/ffi.hpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#ifndef IDEVICE_FFI
|
||||||
|
#define IDEVICE_FFI
|
||||||
|
|
||||||
|
#include <idevice++/bindings.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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
|
||||||
63
cpp/include/idevice++/idevice.hpp
Normal file
63
cpp/include/idevice++/idevice.hpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#ifndef IDEVICE_CPP
|
||||||
|
#define IDEVICE_CPP
|
||||||
|
|
||||||
|
#include <idevice++/ffi.hpp>
|
||||||
|
#include <idevice++/pairing_file.hpp>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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 <class T, void (*FreeFn)(T*)> struct FnDeleter {
|
||||||
|
void operator()(T* p) const noexcept {
|
||||||
|
if (p)
|
||||||
|
FreeFn(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using IdevicePtr = std::unique_ptr<IdeviceHandle, FnDeleter<IdeviceHandle, idevice_free>>;
|
||||||
|
|
||||||
|
class Idevice {
|
||||||
|
public:
|
||||||
|
static std::optional<Idevice>
|
||||||
|
create(IdeviceSocketHandle* socket, const std::string& label, FfiError& err);
|
||||||
|
|
||||||
|
static std::optional<Idevice>
|
||||||
|
create_tcp(const sockaddr* addr, socklen_t addr_len, const std::string& label, FfiError& err);
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
std::optional<std::string> 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
|
||||||
45
cpp/include/idevice++/lockdown.hpp
Normal file
45
cpp/include/idevice++/lockdown.hpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <idevice++/bindings.hpp>
|
||||||
|
#include <idevice++/ffi.hpp>
|
||||||
|
#include <idevice++/provider.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
using LockdownPtr =
|
||||||
|
std::unique_ptr<LockdowndClientHandle, FnDeleter<LockdowndClientHandle, lockdownd_client_free>>;
|
||||||
|
|
||||||
|
class Lockdown {
|
||||||
|
public:
|
||||||
|
// Factory: connect via Provider
|
||||||
|
static std::optional<Lockdown> connect(Provider& provider, FfiError& err);
|
||||||
|
|
||||||
|
// Factory: wrap an existing Idevice socket (consumes it on success)
|
||||||
|
static std::optional<Lockdown> from_socket(Idevice&& socket, FfiError& err);
|
||||||
|
|
||||||
|
// Ops
|
||||||
|
bool start_session(const PairingFile& pf, FfiError& err);
|
||||||
|
std::optional<std::pair<uint16_t, bool>> start_service(const std::string& identifier,
|
||||||
|
FfiError& err);
|
||||||
|
std::optional<plist_t> 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
|
||||||
44
cpp/include/idevice++/pairing_file.hpp
Normal file
44
cpp/include/idevice++/pairing_file.hpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#ifndef IDEVICE_PAIRING_FILE
|
||||||
|
#define IDEVICE_PAIRING_FILE
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <idevice++/ffi.hpp>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
struct PairingFileDeleter {
|
||||||
|
void operator()(IdevicePairingFile* p) const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
using PairingFilePtr = std::unique_ptr<IdevicePairingFile, PairingFileDeleter>;
|
||||||
|
|
||||||
|
class PairingFile {
|
||||||
|
public:
|
||||||
|
static std::optional<PairingFile> read(const std::string& path, FfiError& err);
|
||||||
|
static std::optional<PairingFile> 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<std::vector<uint8_t>> 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
|
||||||
49
cpp/include/idevice++/provider.hpp
Normal file
49
cpp/include/idevice++/provider.hpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <idevice++/bindings.hpp>
|
||||||
|
#include <idevice++/ffi.hpp>
|
||||||
|
#include <idevice++/usbmuxd.hpp>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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<IdeviceProviderHandle, FnDeleter<IdeviceProviderHandle, idevice_provider_free>>;
|
||||||
|
|
||||||
|
class Provider {
|
||||||
|
public:
|
||||||
|
static std::optional<Provider> tcp_new(const idevice_sockaddr* ip,
|
||||||
|
PairingFile&& pairing,
|
||||||
|
const std::string& label,
|
||||||
|
FfiError& err);
|
||||||
|
|
||||||
|
static std::optional<Provider> 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
|
||||||
127
cpp/include/idevice++/usbmuxd.hpp
Normal file
127
cpp/include/idevice++/usbmuxd.hpp
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#ifndef IDEVICE_USBMUXD_HPP
|
||||||
|
#define IDEVICE_USBMUXD_HPP
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <idevice++/idevice.hpp>
|
||||||
|
#include <idevice++/pairing_file.hpp>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#else
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
using AddrPtr =
|
||||||
|
std::unique_ptr<UsbmuxdAddrHandle, FnDeleter<UsbmuxdAddrHandle, idevice_usbmuxd_addr_free>>;
|
||||||
|
using DevicePtr = std::unique_ptr<UsbmuxdDeviceHandle,
|
||||||
|
FnDeleter<UsbmuxdDeviceHandle, idevice_usbmuxd_device_free>>;
|
||||||
|
using ConnectionPtr =
|
||||||
|
std::unique_ptr<UsbmuxdConnectionHandle,
|
||||||
|
FnDeleter<UsbmuxdConnectionHandle, idevice_usbmuxd_connection_free>>;
|
||||||
|
|
||||||
|
class UsbmuxdAddr {
|
||||||
|
public:
|
||||||
|
static std::optional<UsbmuxdAddr>
|
||||||
|
tcp_new(const sockaddr* addr, socklen_t addr_len, FfiError& err);
|
||||||
|
#if defined(__unix__) || defined(__APPLE__)
|
||||||
|
static std::optional<UsbmuxdAddr> 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<Value>(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<std::string> get_udid() const;
|
||||||
|
std::optional<uint32_t> get_id() const;
|
||||||
|
std::optional<UsbmuxdConnectionType> 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<UsbmuxdConnection>
|
||||||
|
tcp_new(const idevice_sockaddr* addr, idevice_socklen_t addr_len, uint32_t tag, FfiError& err);
|
||||||
|
#if defined(__unix__) || defined(__APPLE__)
|
||||||
|
static std::optional<UsbmuxdConnection>
|
||||||
|
unix_new(const std::string& path, uint32_t tag, FfiError& err);
|
||||||
|
#endif
|
||||||
|
static std::optional<UsbmuxdConnection> 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<std::vector<UsbmuxdDevice>> get_devices(FfiError& err) const;
|
||||||
|
std::optional<std::string> get_buid(FfiError& err) const;
|
||||||
|
std::optional<PairingFile> get_pair_record(const std::string& udid, FfiError& err);
|
||||||
|
|
||||||
|
std::optional<Idevice>
|
||||||
|
connect_to_device(uint32_t device_id, uint16_t port, const std::string& path, FfiError& err) &&;
|
||||||
|
std::optional<Idevice>
|
||||||
|
connect_to_device(uint32_t, uint16_t, const std::string&, FfiError&) & = delete;
|
||||||
|
std::optional<Idevice>
|
||||||
|
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
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
// Jackson Coxson
|
|
||||||
|
|
||||||
#ifndef IDEVICE_PAIRING_FILE
|
|
||||||
#define IDEVICE_PAIRING_FILE
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ffi.hpp"
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace IdeviceFFI {
|
|
||||||
class PairingFile {
|
|
||||||
public:
|
|
||||||
static std::optional<PairingFile> 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<PairingFile> 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<std::vector<uint8_t>> 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<uint8_t> 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
|
|
||||||
@@ -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 <cstdint>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#else
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#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<UsbmuxdAddr>
|
|
||||||
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<UsbmuxdAddr> 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<Value>(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<std::string> 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<uint32_t> get_id() {
|
|
||||||
uint32_t id = idevice_usbmuxd_device_get_device_id(handle_);
|
|
||||||
if (id == 0) {
|
|
||||||
return std::nullopt;
|
|
||||||
} else {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<UsbmuxdConnectionType> get_connection_type() {
|
|
||||||
u_int8_t t = idevice_usbmuxd_device_get_connection_type(handle_);
|
|
||||||
if (t == 0) {
|
|
||||||
return std::nullopt;
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
return static_cast<UsbmuxdConnectionType>(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<UsbmuxdConnection>
|
|
||||||
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<UsbmuxdConnection>
|
|
||||||
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<UsbmuxdConnection> 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<std::vector<UsbmuxdDevice>> 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<UsbmuxdDevice> 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<std::string> 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<PairingFile> 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<Idevice>
|
|
||||||
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
|
|
||||||
17
cpp/src/ffi.cpp
Normal file
17
cpp/src/ffi.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#include <idevice++/bindings.hpp>
|
||||||
|
#include <idevice++/ffi.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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<IdeviceFfiError*>(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FfiError::FfiError() : code(0), message("") {
|
||||||
|
}
|
||||||
|
} // namespace IdeviceFFI
|
||||||
56
cpp/src/idevice.cpp
Normal file
56
cpp/src/idevice.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#include <idevice++/idevice.hpp>
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
std::optional<Idevice>
|
||||||
|
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> 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<std::string> 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
|
||||||
67
cpp/src/lockdown.cpp
Normal file
67
cpp/src/lockdown.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#include <idevice++/bindings.hpp>
|
||||||
|
#include <idevice++/ffi.hpp>
|
||||||
|
#include <idevice++/lockdown.hpp>
|
||||||
|
#include <idevice++/provider.hpp>
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
std::optional<Lockdown> 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> 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<std::pair<uint16_t, bool>> 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<plist_t> 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
|
||||||
53
cpp/src/pairing_file.cpp
Normal file
53
cpp/src/pairing_file.cpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#include <idevice++/bindings.hpp>
|
||||||
|
#include <idevice++/ffi.hpp>
|
||||||
|
#include <idevice++/pairing_file.hpp>
|
||||||
|
|
||||||
|
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> 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>
|
||||||
|
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<std::vector<uint8_t>> 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<uint8_t> out(data, data + size);
|
||||||
|
idevice_data_free(data, size);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace IdeviceFFI
|
||||||
52
cpp/src/provider.cpp
Normal file
52
cpp/src/provider.cpp
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#include <idevice++/bindings.hpp>
|
||||||
|
#include <idevice++/ffi.hpp>
|
||||||
|
#include <idevice++/provider.hpp>
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
std::optional<Provider> 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<IdevicePairingFile*>(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> 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<UsbmuxdAddrHandle*>(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
|
||||||
161
cpp/src/usbmuxd.cpp
Normal file
161
cpp/src/usbmuxd.cpp
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
// Jackson Coxson
|
||||||
|
|
||||||
|
#include <idevice++/ffi.hpp>
|
||||||
|
#include <idevice++/usbmuxd.hpp>
|
||||||
|
|
||||||
|
namespace IdeviceFFI {
|
||||||
|
|
||||||
|
// ---------- UsbmuxdAddr ----------
|
||||||
|
std::optional<UsbmuxdAddr>
|
||||||
|
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> 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<std::string> 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<uint32_t> 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<UsbmuxdConnectionType> 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> 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>
|
||||||
|
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> 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<std::vector<UsbmuxdDevice>> 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<UsbmuxdDevice> out;
|
||||||
|
out.reserve(count);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
out.emplace_back(UsbmuxdDevice::adopt(list[i]));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> 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<PairingFile> 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<Idevice> 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
|
||||||
@@ -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
|
|
||||||
@@ -315,3 +315,18 @@ pub unsafe extern "C" fn idevice_string_free(string: *mut c_char) {
|
|||||||
let _ = unsafe { CString::from_raw(string) };
|
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) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use crate::{
|
|||||||
|
|
||||||
pub struct LockdowndClientHandle(pub LockdownClient);
|
pub struct LockdowndClientHandle(pub LockdownClient);
|
||||||
|
|
||||||
/// Connects to lockdownd service using TCP provider
|
/// Connects to lockdownd service using provider
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * [`provider`] - An IdeviceProvider
|
/// * [`provider`] - An IdeviceProvider
|
||||||
|
|||||||
@@ -428,6 +428,26 @@ pub unsafe extern "C" fn idevice_usbmuxd_unix_addr_new(
|
|||||||
null_mut()
|
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
|
/// Frees a UsbmuxdAddr handle
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
|||||||
Reference in New Issue
Block a user