mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 06:26:15 +01:00
Bindings for providers and usbmuxd
This commit is contained in:
150
ffi/src/provider.rs
Normal file
150
ffi/src/provider.rs
Normal file
@@ -0,0 +1,150 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use idevice::provider::{TcpProvider, UsbmuxdProvider};
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use crate::{IdeviceErrorCode, usbmuxd::UsbmuxdAddrHandle, util};
|
||||
|
||||
pub struct TcpProviderHandle(pub TcpProvider);
|
||||
pub struct UsbmuxdProviderHandle(pub UsbmuxdProvider);
|
||||
|
||||
/// Creates a TCP provider for idevice
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`ip`] - The sockaddr IP to connect to
|
||||
/// * [`pairing_file`] - The pairing file handle to use
|
||||
/// * [`label`] - The label to use with the connection
|
||||
/// * [`provider`] - A pointer to a newly allocated provider
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `ip` must be a valid sockaddr
|
||||
/// `pairing_file` must never be used again
|
||||
/// `label` must be a valid Cstr
|
||||
/// `provider` must be a valid, non-null pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn idevice_tcp_provider_new(
|
||||
ip: *const libc::sockaddr,
|
||||
pairing_file: *mut crate::pairing_file::IdevicePairingFile,
|
||||
label: *const c_char,
|
||||
provider: *mut *mut TcpProviderHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
let addr = match util::c_addr_to_rust(ip) {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
return e;
|
||||
}
|
||||
};
|
||||
let label = match unsafe { CStr::from_ptr(label) }.to_str() {
|
||||
Ok(l) => l.to_string(),
|
||||
Err(e) => {
|
||||
log::error!("Invalid label string: {e:?}");
|
||||
return IdeviceErrorCode::InvalidString;
|
||||
}
|
||||
};
|
||||
|
||||
let pairing_file = unsafe { Box::from_raw(pairing_file) };
|
||||
let t = TcpProvider {
|
||||
addr,
|
||||
pairing_file: pairing_file.0,
|
||||
label,
|
||||
};
|
||||
|
||||
let boxed = Box::new(TcpProviderHandle(t));
|
||||
unsafe { *provider = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
/// Frees a TcpProvider handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - The provider handle to free
|
||||
///
|
||||
/// # Safety
|
||||
/// `provider` must be a valid pointer to a TcpProvider handle that was allocated by this library,
|
||||
/// or NULL (in which case this function does nothing)
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn tcp_provider_free(provider: *mut TcpProvider) {
|
||||
if !provider.is_null() {
|
||||
unsafe { drop(Box::from_raw(provider)) };
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a usbmuxd provider for idevice
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`addr`] - The UsbmuxdAddr handle to connect to
|
||||
/// * [`tag`] - The tag returned in usbmuxd responses
|
||||
/// * [`udid`] - The UDID of the device to connect to
|
||||
/// * [`device_id`] - The muxer ID of the device to connect to
|
||||
/// * [`pairing_file`] - The pairing file handle to use
|
||||
/// * [`label`] - The label to use with the connection
|
||||
/// * [`provider`] - A pointer to a newly allocated provider
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `addr` must be a valid pointer to UsbmuxdAddrHandle created by this library, and never used again
|
||||
/// `udid` must be a valid CStr
|
||||
/// `pairing_file` must never be used again
|
||||
/// `label` must be a valid Cstr
|
||||
/// `provider` must be a valid, non-null pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn usbmuxd_provider_new(
|
||||
addr: *mut UsbmuxdAddrHandle,
|
||||
tag: u32,
|
||||
udid: *const c_char,
|
||||
device_id: u32,
|
||||
label: *const c_char,
|
||||
provider: *mut *mut UsbmuxdProviderHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
let udid = match unsafe { CStr::from_ptr(udid) }.to_str() {
|
||||
Ok(u) => u.to_string(),
|
||||
Err(e) => {
|
||||
log::error!("Invalid UDID string: {e:?}");
|
||||
return IdeviceErrorCode::InvalidArgument;
|
||||
}
|
||||
};
|
||||
|
||||
let label = match unsafe { CStr::from_ptr(label) }.to_str() {
|
||||
Ok(l) => l.to_string(),
|
||||
Err(e) => {
|
||||
log::error!("Invalid UDID string: {e:?}");
|
||||
return IdeviceErrorCode::InvalidArgument;
|
||||
}
|
||||
};
|
||||
|
||||
let addr = unsafe { Box::from_raw(addr) }.0;
|
||||
|
||||
let p = UsbmuxdProvider {
|
||||
addr,
|
||||
tag,
|
||||
udid,
|
||||
device_id,
|
||||
label,
|
||||
};
|
||||
|
||||
let boxed = Box::new(UsbmuxdProviderHandle(p));
|
||||
unsafe { *provider = Box::into_raw(boxed) };
|
||||
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
/// Frees a UsbmuxdProvider handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`provider`] - The provider handle to free
|
||||
///
|
||||
/// # Safety
|
||||
/// `provider` must be a valid pointer to a UsbmuxdProvider handle that was allocated by this library,
|
||||
/// or NULL (in which case this function does nothing)
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn usbmuxd_provider_free(provider: *mut UsbmuxdProvider) {
|
||||
if !provider.is_null() {
|
||||
unsafe { drop(Box::from_raw(provider)) };
|
||||
}
|
||||
}
|
||||
188
ffi/src/usbmuxd.rs
Normal file
188
ffi/src/usbmuxd.rs
Normal file
@@ -0,0 +1,188 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use std::ffi::{CStr, c_char};
|
||||
|
||||
use crate::{IdeviceErrorCode, RUNTIME, util::c_socket_to_rust};
|
||||
use idevice::{
|
||||
IdeviceError,
|
||||
usbmuxd::{UsbmuxdAddr, UsbmuxdConnection},
|
||||
};
|
||||
|
||||
pub struct UsbmuxdConnectionHandle(pub UsbmuxdConnection);
|
||||
pub struct UsbmuxdAddrHandle(pub UsbmuxdAddr);
|
||||
|
||||
/// Connects to a usbmuxd instance over TCP
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`addr`] - The socket address to connect to
|
||||
/// * [`addr_len`] - Length of the socket
|
||||
/// * [`tag`] - A tag that will be returned by usbmuxd responses
|
||||
/// * [`usbmuxd_connection`] - On success, will be set to point to a newly allocated UsbmuxdConnection handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `addr` must be a valid sockaddr
|
||||
/// `label` must be a valid null-terminated C string
|
||||
/// `usbmuxd_connection` 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_new_tcp_connection(
|
||||
addr: *const libc::sockaddr,
|
||||
addr_len: libc::socklen_t,
|
||||
tag: u32,
|
||||
usbmuxd_connection: *mut *mut UsbmuxdConnectionHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
let addr = match c_socket_to_rust(addr, addr_len) {
|
||||
Ok(a) => a,
|
||||
Err(e) => return e,
|
||||
};
|
||||
|
||||
let res: Result<UsbmuxdConnection, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let stream = tokio::net::TcpStream::connect(addr).await?;
|
||||
Ok(UsbmuxdConnection::new(Box::new(stream), tag))
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
let boxed = Box::new(UsbmuxdConnectionHandle(r));
|
||||
unsafe { *usbmuxd_connection = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Connects to a usbmuxd instance over unix socket
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`addr`] - The socket path to connect to
|
||||
/// * [`tag`] - A tag that will be returned by usbmuxd responses
|
||||
/// * [`usbmuxd_connection`] - On success, will be set to point to a newly allocated UsbmuxdConnection handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `addr` must be a valid CStr
|
||||
/// `label` must be a valid null-terminated C string
|
||||
/// `usbmuxd_connection` must be a valid, non-null pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
#[cfg(unix)]
|
||||
pub unsafe extern "C" fn idevice_usbmuxd_new_unix_socket_connection(
|
||||
addr: *const c_char,
|
||||
tag: u32,
|
||||
usbmuxd_connection: *mut *mut UsbmuxdConnectionHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
let addr = match unsafe { CStr::from_ptr(addr).to_str() } {
|
||||
Ok(s) => s,
|
||||
Err(_) => return IdeviceErrorCode::InvalidArg,
|
||||
};
|
||||
|
||||
let res: Result<UsbmuxdConnection, IdeviceError> = RUNTIME.block_on(async move {
|
||||
let stream = tokio::net::UnixStream::connect(addr).await?;
|
||||
Ok(UsbmuxdConnection::new(Box::new(stream), tag))
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
let boxed = Box::new(UsbmuxdConnectionHandle(r));
|
||||
unsafe { *usbmuxd_connection = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
Err(e) => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees a UsbmuxdConnection handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`usbmuxd_connection`] - The UsbmuxdConnection handle to free
|
||||
///
|
||||
/// # Safety
|
||||
/// `usbmuxd_connection` must be a valid pointer to a UsbmuxdConnection handle that was allocated by this library,
|
||||
/// or NULL (in which case this function does nothing)
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn idevice_usbmuxd_connection_free(
|
||||
usbmuxd_connection: *mut UsbmuxdConnectionHandle,
|
||||
) {
|
||||
if !usbmuxd_connection.is_null() {
|
||||
let _ = unsafe { Box::from_raw(usbmuxd_connection) };
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a usbmuxd TCP address struct
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`addr`] - The socket address to connect to
|
||||
/// * [`addr_len`] - Length of the socket
|
||||
/// * [`usbmuxd_addr`] - On success, will be set to point to a newly allocated UsbmuxdAddr handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `addr` must be a valid sockaddr
|
||||
/// `usbmuxd_Addr` must be a valid, non-null pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn idevice_usbmuxd_tcp_addr_new(
|
||||
addr: *const libc::sockaddr,
|
||||
addr_len: libc::socklen_t,
|
||||
usbmuxd_addr: *mut *mut UsbmuxdAddrHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
let addr = match c_socket_to_rust(addr, addr_len) {
|
||||
Ok(a) => a,
|
||||
Err(e) => return e,
|
||||
};
|
||||
|
||||
let u = UsbmuxdAddr::TcpSocket(addr);
|
||||
|
||||
let boxed = Box::new(UsbmuxdAddrHandle(u));
|
||||
unsafe { *usbmuxd_addr = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
/// Creates a new UsbmuxdAddr struct with a unix socket
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`addr`] - The socket path to connect to
|
||||
/// * [`usbmuxd_addr`] - On success, will be set to point to a newly allocated UsbmuxdAddr handle
|
||||
///
|
||||
/// # Returns
|
||||
/// An error code indicating success or failure
|
||||
///
|
||||
/// # Safety
|
||||
/// `addr` must be a valid CStr
|
||||
/// `usbmuxd_addr` must be a valid, non-null pointer to a location where the handle will be stored
|
||||
#[unsafe(no_mangle)]
|
||||
#[cfg(unix)]
|
||||
pub unsafe extern "C" fn idevice_usbmuxd_unix_addr_new(
|
||||
addr: *const c_char,
|
||||
usbmuxd_addr: *mut *mut UsbmuxdAddrHandle,
|
||||
) -> IdeviceErrorCode {
|
||||
let addr = match unsafe { CStr::from_ptr(addr).to_str() } {
|
||||
Ok(s) => s,
|
||||
Err(_) => return IdeviceErrorCode::InvalidArg,
|
||||
};
|
||||
|
||||
let u = UsbmuxdAddr::UnixSocket(addr.to_string());
|
||||
|
||||
let boxed = Box::new(UsbmuxdAddrHandle(u));
|
||||
unsafe { *usbmuxd_addr = Box::into_raw(boxed) };
|
||||
IdeviceErrorCode::IdeviceSuccess
|
||||
}
|
||||
|
||||
/// Frees a UsbmuxdAddr handle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * [`usbmuxd_addr`] - The UsbmuxdAddr handle to free
|
||||
///
|
||||
/// # Safety
|
||||
/// `usbmuxd_addr` must be a valid pointer to a UsbmuxdAddr handle that was allocated by this library,
|
||||
/// or NULL (in which case this function does nothing)
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn idevice_usbmuxd_addr_free(usbmuxd_addr: *mut UsbmuxdAddrHandle) {
|
||||
if !usbmuxd_addr.is_null() {
|
||||
let _ = unsafe { Box::from_raw(usbmuxd_addr) };
|
||||
}
|
||||
}
|
||||
77
ffi/src/util.rs
Normal file
77
ffi/src/util.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use std::{
|
||||
ffi::c_int,
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
};
|
||||
|
||||
use libc::{sockaddr_in, sockaddr_in6};
|
||||
|
||||
use crate::IdeviceErrorCode;
|
||||
|
||||
pub(crate) fn c_socket_to_rust(
|
||||
addr: *const libc::sockaddr,
|
||||
addr_len: libc::socklen_t,
|
||||
) -> Result<SocketAddr, IdeviceErrorCode> {
|
||||
Ok(unsafe {
|
||||
match (*addr).sa_family as c_int {
|
||||
libc::AF_INET => {
|
||||
if (addr_len as usize) < std::mem::size_of::<sockaddr_in>() {
|
||||
log::error!("Invalid sockaddr_in size");
|
||||
return Err(IdeviceErrorCode::InvalidArg);
|
||||
}
|
||||
let addr_in = *(addr as *const sockaddr_in);
|
||||
let ip = std::net::Ipv4Addr::from(u32::from_be(addr_in.sin_addr.s_addr));
|
||||
let port = u16::from_be(addr_in.sin_port);
|
||||
std::net::SocketAddr::V4(std::net::SocketAddrV4::new(ip, port))
|
||||
}
|
||||
libc::AF_INET6 => {
|
||||
if addr_len as usize >= std::mem::size_of::<sockaddr_in6>() {
|
||||
let addr_in6 = *(addr as *const sockaddr_in6);
|
||||
let ip = std::net::Ipv6Addr::from(addr_in6.sin6_addr.s6_addr);
|
||||
let port = u16::from_be(addr_in6.sin6_port);
|
||||
std::net::SocketAddr::V6(std::net::SocketAddrV6::new(
|
||||
ip,
|
||||
port,
|
||||
addr_in6.sin6_flowinfo,
|
||||
addr_in6.sin6_scope_id,
|
||||
))
|
||||
} else {
|
||||
log::error!("Invalid sockaddr_in6 size");
|
||||
return Err(IdeviceErrorCode::InvalidArg);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
log::error!("Unsupported socket address family: {}", (*addr).sa_family);
|
||||
return Err(IdeviceErrorCode::InvalidArg);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn c_addr_to_rust(addr: *const libc::sockaddr) -> Result<IpAddr, IdeviceErrorCode> {
|
||||
unsafe {
|
||||
// Check the address family
|
||||
match (*addr).sa_family as c_int {
|
||||
libc::AF_INET => {
|
||||
// Convert sockaddr_in (IPv4) to IpAddr
|
||||
let sockaddr_in = addr as *const sockaddr_in;
|
||||
let ip = (*sockaddr_in).sin_addr.s_addr;
|
||||
let octets = u32::from_be(ip).to_be_bytes();
|
||||
Ok(IpAddr::V4(Ipv4Addr::new(
|
||||
octets[0], octets[1], octets[2], octets[3],
|
||||
)))
|
||||
}
|
||||
libc::AF_INET6 => {
|
||||
// Convert sockaddr_in6 (IPv6) to IpAddr
|
||||
let sockaddr_in6 = addr as *const sockaddr_in6;
|
||||
let ip = (*sockaddr_in6).sin6_addr.s6_addr;
|
||||
Ok(IpAddr::V6(Ipv6Addr::from(ip)))
|
||||
}
|
||||
_ => {
|
||||
log::error!("Unsupported socket address family: {}", (*addr).sa_family);
|
||||
Err(IdeviceErrorCode::InvalidArg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user