Use platform-independent socket for FFI

Windows is truly awful

Remove config.toml
This commit is contained in:
Jackson Coxson
2025-08-12 11:35:13 -06:00
parent 90786e9577
commit 618500fd0c
7 changed files with 235 additions and 90 deletions

View File

@@ -136,22 +136,23 @@ pub unsafe extern "C" fn idevice_new(
/// `addr` must be a valid sockaddr
/// `label` must be a valid null-terminated C string
/// `idevice` must be a valid, non-null pointer to a location where the handle will be stored
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn idevice_new_tcp_socket(
addr: *const libc::sockaddr,
addr_len: libc::socklen_t,
addr: *const idevice_sockaddr,
addr_len: idevice_socklen_t,
label: *const c_char,
idevice: *mut *mut IdeviceHandle,
) -> *mut IdeviceFfiError {
if addr.is_null() {
log::error!("socket addr null pointer");
if addr.is_null() || label.is_null() || idevice.is_null() {
log::error!("null pointer(s) to idevice_new_tcp_socket");
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let addr = addr as *const SockAddr;
// Convert C string to Rust string
let label = match unsafe { CStr::from_ptr(label).to_str() } {
Ok(s) => s,
Err(_) => return ffi_err!(IdeviceError::FfiInvalidArg),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
};
let addr = match util::c_socket_to_rust(addr, addr_len) {
@@ -159,9 +160,10 @@ pub unsafe extern "C" fn idevice_new_tcp_socket(
Err(e) => return ffi_err!(e),
};
let device: Result<idevice::Idevice, idevice::IdeviceError> = RUNTIME.block_on(async move {
Ok(idevice::Idevice::new(
Box::new(tokio::net::TcpStream::connect(addr).await?),
let device = RUNTIME.block_on(async move {
let stream = tokio::net::TcpStream::connect(addr).await?;
Ok::<idevice::Idevice, idevice::IdeviceError>(idevice::Idevice::new(
Box::new(stream),
label,
))
});
@@ -170,7 +172,7 @@ pub unsafe extern "C" fn idevice_new_tcp_socket(
Ok(dev) => {
let boxed = Box::new(IdeviceHandle(dev));
unsafe { *idevice = Box::into_raw(boxed) };
null_mut()
std::ptr::null_mut()
}
Err(e) => ffi_err!(e),
}

View File

@@ -1,9 +1,11 @@
// Jackson Coxson
use idevice::provider::{IdeviceProvider, TcpProvider, UsbmuxdProvider};
use std::net::IpAddr;
use std::os::raw::c_char;
use std::{ffi::CStr, ptr::null_mut};
use crate::util::{SockAddr, idevice_sockaddr};
use crate::{IdeviceFfiError, ffi_err, usbmuxd::UsbmuxdAddrHandle, util};
pub struct IdeviceProviderHandle(pub Box<dyn IdeviceProvider>);
@@ -24,33 +26,27 @@ pub struct IdeviceProviderHandle(pub Box<dyn IdeviceProvider>);
/// `pairing_file` is consumed must never be used again
/// `label` must be a valid Cstr
/// `provider` must be a valid, non-null pointer to a location where the handle will be stored
#[cfg(feature = "tcp")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn idevice_tcp_provider_new(
ip: *const libc::sockaddr,
ip: *const idevice_sockaddr,
pairing_file: *mut crate::pairing_file::IdevicePairingFile,
label: *const c_char,
provider: *mut *mut IdeviceProviderHandle,
) -> *mut IdeviceFfiError {
if ip.is_null() || label.is_null() || provider.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
let addr = match util::c_addr_to_rust(ip) {
let ip = ip as *const SockAddr;
let addr: IpAddr = match util::c_addr_to_rust(ip) {
Ok(i) => i,
Err(e) => {
return ffi_err!(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 ffi_err!(IdeviceError::FfiInvalidString);
}
Err(e) => return ffi_err!(e),
};
let label = match unsafe { CStr::from_ptr(label).to_str() } {
Ok(s) => s.to_string(),
Err(_) => return ffi_err!(IdeviceError::FfiInvalidString),
};
// consume the pairing file on success
let pairing_file = unsafe { Box::from_raw(pairing_file) };
let t = TcpProvider {
addr,
pairing_file: pairing_file.0,
@@ -59,7 +55,7 @@ pub unsafe extern "C" fn idevice_tcp_provider_new(
let boxed = Box::new(IdeviceProviderHandle(Box::new(t)));
unsafe { *provider = Box::into_raw(boxed) };
null_mut()
std::ptr::null_mut()
}
/// Frees an IdeviceProvider handle

View File

@@ -6,7 +6,8 @@ use std::{
};
use crate::{
IdeviceFfiError, IdeviceHandle, IdevicePairingFile, RUNTIME, ffi_err, util::c_socket_to_rust,
IdeviceFfiError, IdeviceHandle, IdevicePairingFile, RUNTIME, ffi_err,
util::{SockAddr, c_socket_to_rust, idevice_sockaddr, idevice_socklen_t},
};
use idevice::{
IdeviceError,
@@ -32,28 +33,33 @@ pub struct UsbmuxdDeviceHandle(pub UsbmuxdDevice);
/// # Safety
/// `addr` must be a valid sockaddr
/// `usbmuxd_connection` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)]
pub unsafe extern "C" fn idevice_usbmuxd_new_tcp_connection(
addr: *const libc::sockaddr,
addr_len: libc::socklen_t,
addr: *const idevice_sockaddr,
addr_len: idevice_socklen_t,
tag: u32,
usbmuxd_connection: *mut *mut UsbmuxdConnectionHandle,
out: *mut *mut UsbmuxdConnectionHandle,
) -> *mut IdeviceFfiError {
let addr = match c_socket_to_rust(addr, addr_len) {
if addr.is_null() || out.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
// Reinterpret as the real platform sockaddr for parsing
let addr = addr as *const SockAddr;
let addr = match c_socket_to_rust(addr, addr_len as _) {
Ok(a) => a,
Err(e) => return ffi_err!(e),
};
let res: Result<UsbmuxdConnection, IdeviceError> = RUNTIME.block_on(async move {
let res = RUNTIME.block_on(async move {
let stream = tokio::net::TcpStream::connect(addr).await?;
Ok(UsbmuxdConnection::new(Box::new(stream), tag))
Ok::<_, IdeviceError>(UsbmuxdConnection::new(Box::new(stream), tag))
});
match res {
Ok(r) => {
let boxed = Box::new(UsbmuxdConnectionHandle(r));
unsafe { *usbmuxd_connection = Box::into_raw(boxed) };
null_mut()
Ok(conn) => {
unsafe { *out = Box::into_raw(Box::new(UsbmuxdConnectionHandle(conn))) };
std::ptr::null_mut()
}
Err(e) => ffi_err!(e),
}
@@ -367,20 +373,28 @@ pub unsafe extern "C" fn idevice_usbmuxd_connection_free(
/// `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,
addr: *const idevice_sockaddr, // <- portable
addr_len: idevice_socklen_t,
usbmuxd_addr: *mut *mut UsbmuxdAddrHandle,
) -> *mut IdeviceFfiError {
let addr = match c_socket_to_rust(addr, addr_len) {
if addr.is_null() || usbmuxd_addr.is_null() {
return ffi_err!(IdeviceError::FfiInvalidArg);
}
// Reinterpret as the real platform sockaddr for parsing
let addr = addr as *const SockAddr;
let addr = match c_socket_to_rust(addr, addr_len as _) {
Ok(a) => a,
Err(e) => return ffi_err!(e),
};
let u = UsbmuxdAddr::TcpSocket(addr);
let boxed = Box::new(UsbmuxdAddrHandle(u));
unsafe { *usbmuxd_addr = Box::into_raw(boxed) };
null_mut()
unsafe {
*usbmuxd_addr = Box::into_raw(boxed);
}
std::ptr::null_mut()
}
/// Creates a new UsbmuxdAddr struct with a unix socket

View File

@@ -1,75 +1,185 @@
// Jackson Coxson
use std::{
ffi::c_int,
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use idevice::IdeviceError;
use libc::{sockaddr_in, sockaddr_in6};
// portable FFI-facing types (only used in signatures)
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct idevice_sockaddr {
_priv: [u8; 0], // opaque; acts as "struct sockaddr" placeholder
}
#[cfg(unix)]
#[allow(non_camel_case_types)]
pub type idevice_socklen_t = libc::socklen_t;
#[cfg(windows)]
#[allow(non_camel_case_types)]
pub type idevice_socklen_t = i32;
// platform sockaddr aliases for implementation
#[cfg(unix)]
pub(crate) type SockAddr = libc::sockaddr;
#[cfg(windows)]
use windows_sys::Win32::Networking::WinSock as winsock;
#[cfg(windows)]
pub(crate) type SockAddr = winsock::SOCKADDR;
#[cfg(unix)]
use libc::{self, sockaddr_in, sockaddr_in6};
#[cfg(windows)]
use windows_sys::Win32::Networking::WinSock::{
AF_INET, AF_INET6, SOCKADDR_IN as sockaddr_in, SOCKADDR_IN6 as sockaddr_in6,
};
#[cfg(unix)]
type SockLen = libc::socklen_t;
#[cfg(windows)]
type SockLen = i32; // socklen_t is an int on Windows
#[inline]
fn invalid_arg<T>() -> Result<T, IdeviceError> {
Err(IdeviceError::FfiInvalidArg)
}
pub(crate) fn c_socket_to_rust(
addr: *const libc::sockaddr,
addr_len: libc::socklen_t,
addr: *const SockAddr,
addr_len: SockLen,
) -> Result<SocketAddr, IdeviceError> {
Ok(unsafe {
match (*addr).sa_family as c_int {
if addr.is_null() {
log::error!("null sockaddr");
return invalid_arg();
}
unsafe {
let family = (*addr).sa_family;
#[cfg(unix)]
match family as i32 {
libc::AF_INET => {
if (addr_len as usize) < std::mem::size_of::<sockaddr_in>() {
log::error!("Invalid sockaddr_in size");
return Err(IdeviceError::FfiInvalidArg);
return invalid_arg();
}
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))
let a = &*(addr as *const sockaddr_in);
let ip = Ipv4Addr::from(u32::from_be(a.sin_addr.s_addr));
let port = u16::from_be(a.sin_port);
Ok(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 {
if (addr_len as usize) < std::mem::size_of::<sockaddr_in6>() {
log::error!("Invalid sockaddr_in6 size");
return Err(IdeviceError::FfiInvalidArg);
return invalid_arg();
}
let a = &*(addr as *const sockaddr_in6);
let ip = Ipv6Addr::from(a.sin6_addr.s6_addr);
let port = u16::from_be(a.sin6_port);
Ok(SocketAddr::V6(std::net::SocketAddrV6::new(
ip,
port,
a.sin6_flowinfo,
a.sin6_scope_id,
)))
}
_ => {
log::error!(
"Unsupported socket address family: {}",
(*addr).sa_family as i32
);
invalid_arg()
}
}
#[cfg(windows)]
match family {
AF_INET => {
if (addr_len as usize) < std::mem::size_of::<sockaddr_in>() {
log::error!("Invalid SOCKADDR_IN size");
return invalid_arg();
}
let a = &*(addr as *const sockaddr_in);
// IN_ADDR is a union; use S_un.S_addr (network byte order)
let ip_be = a.sin_addr.S_un.S_addr;
let ip = Ipv4Addr::from(u32::from_be(ip_be));
let port = u16::from_be(a.sin_port);
Ok(SocketAddr::V4(std::net::SocketAddrV4::new(ip, port)))
}
AF_INET6 => {
if (addr_len as usize) < std::mem::size_of::<sockaddr_in6>() {
log::error!("Invalid SOCKADDR_IN6 size");
return invalid_arg();
}
let a = &*(addr as *const sockaddr_in6);
// IN6_ADDR is a union; read the 16 Byte array
let bytes: [u8; 16] = a.sin6_addr.u.Byte;
let ip = Ipv6Addr::from(bytes);
let port = u16::from_be(a.sin6_port);
let scope_id = a.Anonymous.sin6_scope_id;
Ok(SocketAddr::V6(std::net::SocketAddrV6::new(
ip,
port,
a.sin6_flowinfo,
scope_id,
)))
}
_ => {
log::error!("Unsupported socket address family: {}", (*addr).sa_family);
return Err(IdeviceError::FfiInvalidArg);
invalid_arg()
}
}
})
}
}
pub(crate) fn c_addr_to_rust(addr: *const libc::sockaddr) -> Result<IpAddr, IdeviceError> {
pub(crate) fn c_addr_to_rust(addr: *const SockAddr) -> Result<IpAddr, IdeviceError> {
if addr.is_null() {
log::error!("null sockaddr");
return invalid_arg();
}
unsafe {
// Check the address family
match (*addr).sa_family as c_int {
#[cfg(unix)]
let family = (*addr).sa_family as i32;
#[cfg(windows)]
let family = (*addr).sa_family;
#[cfg(unix)]
match family {
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();
let a = &*(addr as *const sockaddr_in);
let octets = u32::from_be(a.sin_addr.s_addr).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)))
let a = &*(addr as *const sockaddr_in6);
Ok(IpAddr::V6(Ipv6Addr::from(a.sin6_addr.s6_addr)))
}
_ => {
log::error!(
"Unsupported socket address family: {}",
(*addr).sa_family as i32
);
invalid_arg()
}
}
#[cfg(windows)]
match family {
AF_INET => {
let a = &*(addr as *const sockaddr_in);
let ip_be = a.sin_addr.S_un.S_addr;
Ok(IpAddr::V4(Ipv4Addr::from(u32::from_be(ip_be))))
}
AF_INET6 => {
let a = &*(addr as *const sockaddr_in6);
let bytes: [u8; 16] = a.sin6_addr.u.Byte;
Ok(IpAddr::V6(Ipv6Addr::from(bytes)))
}
_ => {
log::error!("Unsupported socket address family: {}", (*addr).sa_family);
Err(IdeviceError::FfiInvalidArg)
invalid_arg()
}
}
}