From e256cb72dc9f3b25d9693066f37a5ca4124b54d2 Mon Sep 17 00:00:00 2001 From: Abdullah Al-Banna Date: Fri, 17 Apr 2026 10:13:51 +0300 Subject: [PATCH 1/3] Support the IPv6 scope id for network based connections --- Cargo.lock | 5 +++-- ffi/src/provider.rs | 4 ++-- ffi/src/util.rs | 19 ++++++++++++------- idevice/Cargo.toml | 1 + idevice/src/provider.rs | 11 ++++++++++- idevice/src/usbmuxd/des.rs | 8 ++++---- tools/src/main.rs | 12 +++++++++++- 7 files changed, 43 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03382d8e..6631e966 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1288,6 +1288,7 @@ dependencies = [ "indexmap", "jktcp", "json", + "libc", "ns-keyed-archive", "obfstr", "openssl", @@ -1512,9 +1513,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.183" +version = "0.2.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" [[package]] name = "libloading" diff --git a/ffi/src/provider.rs b/ffi/src/provider.rs index 9b544189..38022e98 100644 --- a/ffi/src/provider.rs +++ b/ffi/src/provider.rs @@ -1,7 +1,6 @@ // 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}; @@ -35,7 +34,7 @@ pub unsafe extern "C" fn idevice_tcp_provider_new( provider: *mut *mut IdeviceProviderHandle, ) -> *mut IdeviceFfiError { let ip = ip as *const SockAddr; - let addr: IpAddr = match util::c_addr_to_rust(ip) { + let (addr, scope_id) = match util::c_addr_to_rust(ip) { Ok(i) => i, Err(e) => return ffi_err!(e), }; @@ -50,6 +49,7 @@ pub unsafe extern "C" fn idevice_tcp_provider_new( let t = TcpProvider { addr, + scope_id, pairing_file: pairing_file.0, label, }; diff --git a/ffi/src/util.rs b/ffi/src/util.rs index 0aa496b1..2fecd7e3 100644 --- a/ffi/src/util.rs +++ b/ffi/src/util.rs @@ -131,7 +131,7 @@ pub(crate) fn c_socket_to_rust( } } -pub(crate) fn c_addr_to_rust(addr: *const SockAddr) -> Result { +pub(crate) fn c_addr_to_rust(addr: *const SockAddr) -> Result<(IpAddr, Option), IdeviceError> { if addr.is_null() { tracing::error!("null sockaddr"); return invalid_arg(); @@ -148,13 +148,17 @@ pub(crate) fn c_addr_to_rust(addr: *const SockAddr) -> Result { 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], - ))) + Ok(( + IpAddr::V4(Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3])), + None, + )) } libc::AF_INET6 => { let a = &*(addr as *const sockaddr_in6); - Ok(IpAddr::V6(Ipv6Addr::from(a.sin6_addr.s6_addr))) + Ok(( + IpAddr::V6(Ipv6Addr::from(a.sin6_addr.s6_addr)), + Some(a.sin6_scope_id), + )) } _ => { tracing::error!( @@ -170,12 +174,13 @@ pub(crate) fn c_addr_to_rust(addr: *const SockAddr) -> Result { 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)))) + Ok(IpAddr::V4(Ipv4Addr::from(u32::from_be(ip_be))), None) } AF_INET6 => { let a = &*(addr as *const sockaddr_in6); let bytes: [u8; 16] = a.sin6_addr.u.Byte; - Ok(IpAddr::V6(Ipv6Addr::from(bytes))) + let scope_id: u32 = a.Anonymous.sin6_scope_id; + Ok(IpAddr::V6(Ipv6Addr::from(bytes)), Some(scope_id)) } _ => { tracing::error!("Unsupported socket address family: {}", (*addr).sa_family); diff --git a/idevice/Cargo.toml b/idevice/Cargo.toml index 4172b702..7c67f64d 100644 --- a/idevice/Cargo.toml +++ b/idevice/Cargo.toml @@ -68,6 +68,7 @@ rand = { version = "0.10" } obfstr = { version = "0.4", optional = true } async_zip = { version = "0.0.18", optional = true } +libc = "0.2" [dev-dependencies] tokio = { version = "1.43", features = ["full"] } diff --git a/idevice/src/provider.rs b/idevice/src/provider.rs index 09c12738..195a76b4 100644 --- a/idevice/src/provider.rs +++ b/idevice/src/provider.rs @@ -5,6 +5,9 @@ use std::{future::Future, pin::Pin}; +use std::net::IpAddr; +use std::net::{SocketAddr, SocketAddrV6}; + #[cfg(feature = "tcp")] use tokio::net::TcpStream; @@ -55,6 +58,8 @@ pub trait RsdProvider: Unpin + Send + Sync + std::fmt::Debug { pub struct TcpProvider { /// IP address of the device pub addr: std::net::IpAddr, + /// An optional scope ID may be provided for IPv6 addresses. + pub scope_id: Option, /// Pairing file for secure communication pub pairing_file: PairingFile, /// Label identifying this connection @@ -76,8 +81,12 @@ impl IdeviceProvider for TcpProvider { ) -> Pin> + Send>> { let addr = self.addr; let label = self.label.clone(); + let scope_id = self.scope_id.unwrap_or(0); Box::pin(async move { - let socket_addr = std::net::SocketAddr::new(addr, port); + let socket_addr = match addr { + IpAddr::V4(_) => SocketAddr::new(addr, port), + IpAddr::V6(ipv6) => SocketAddr::V6(SocketAddrV6::new(ipv6, port, 0, scope_id)), + }; let stream = TcpStream::connect(socket_addr).await?; Ok(Idevice::new(Box::new(stream), label)) }) diff --git a/idevice/src/usbmuxd/des.rs b/idevice/src/usbmuxd/des.rs index b968d9fa..adf672c5 100644 --- a/idevice/src/usbmuxd/des.rs +++ b/idevice/src/usbmuxd/des.rs @@ -55,14 +55,14 @@ impl TryFrom for UsbmuxdDevice { )); } - match addr[0] { - 0x02 => { + match addr[0] as libc::c_int { + libc::AF_INET => { // IPv4 Connection::Network(IpAddr::V4(Ipv4Addr::new( addr[4], addr[5], addr[6], addr[7], ))) } - 0x1E => { + libc::AF_INET6 => { // IPv6 if addr.len() < 24 { warn!("IPv6 address is less than 24 bytes"); @@ -91,7 +91,7 @@ impl TryFrom for UsbmuxdDevice { .into(), )); } - if addr[1] == 0x1E { + if addr[1] as libc::c_int == libc::AF_INET6 { // IPv6 address starts at offset 8 in sockaddr_in6 Connection::Network(IpAddr::V6(Ipv6Addr::new( u16::from_be_bytes([addr[8], addr[9]]), diff --git a/tools/src/main.rs b/tools/src/main.rs index 4693dce6..2feef2a8 100644 --- a/tools/src/main.rs +++ b/tools/src/main.rs @@ -330,7 +330,16 @@ async fn get_provider( } else if let Some(host) = host && let Some(pairing_file) = pairing_file { - let host = match IpAddr::from_str(host.as_str()) { + // split host and the optional scope_id (e.g., "fe80::1%3") if IPv6 + let (host_str, scope_id) = match host.rsplit_once('%') { + Some((h, scope_id)) => { + let scope_id = scope_id.parse::().ok(); + (h, scope_id) + } + None => (host.as_str(), None), + }; + + let host = match IpAddr::from_str(host_str) { Ok(h) => h, Err(e) => { return Err(format!("Invalid host: {e:?}")); @@ -345,6 +354,7 @@ async fn get_provider( Box::new(TcpProvider { addr: host, + scope_id, pairing_file, label: label.to_string(), }) From ffe0c9ab17a20ca37b87d1c456d47c1e0bb374e9 Mon Sep 17 00:00:00 2001 From: Abdullah Al-Banna Date: Fri, 17 Apr 2026 10:38:10 +0300 Subject: [PATCH 2/3] windows --- Cargo.lock | 1 + ffi/src/util.rs | 4 ++-- idevice/Cargo.toml | 15 ++++++++++++++- idevice/src/usbmuxd/des.rs | 13 +++++++++---- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6631e966..809e9c72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1309,6 +1309,7 @@ dependencies = [ "tracing", "tun-rs", "uuid", + "windows-sys 0.61.2", "x25519-dalek", "x509-cert", ] diff --git a/ffi/src/util.rs b/ffi/src/util.rs index 2fecd7e3..26dea470 100644 --- a/ffi/src/util.rs +++ b/ffi/src/util.rs @@ -174,13 +174,13 @@ pub(crate) fn c_addr_to_rust(addr: *const SockAddr) -> Result<(IpAddr, Option { 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))), None) + Ok((IpAddr::V4(Ipv4Addr::from(u32::from_be(ip_be))), None)) } AF_INET6 => { let a = &*(addr as *const sockaddr_in6); let bytes: [u8; 16] = a.sin6_addr.u.Byte; let scope_id: u32 = a.Anonymous.sin6_scope_id; - Ok(IpAddr::V6(Ipv6Addr::from(bytes)), Some(scope_id)) + Ok((IpAddr::V6(Ipv6Addr::from(bytes)), Some(scope_id))) } _ => { tracing::error!("Unsupported socket address family: {}", (*addr).sa_family); diff --git a/idevice/Cargo.toml b/idevice/Cargo.toml index 7c67f64d..63300060 100644 --- a/idevice/Cargo.toml +++ b/idevice/Cargo.toml @@ -68,8 +68,12 @@ rand = { version = "0.10" } obfstr = { version = "0.4", optional = true } async_zip = { version = "0.0.18", optional = true } + libc = "0.2" +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.61", features = ["Win32_Networking_WinSock"] } + [dev-dependencies] tokio = { version = "1.43", features = ["full"] } tun-rs = { version = "2.0.8", features = ["async_tokio"] } @@ -109,7 +113,16 @@ installation_proxy = [ "tokio/fs", ] installcoordination_proxy = [] -xctest = ["dvt", "installation_proxy", "afc", "dep:uuid", "dep:ns-keyed-archive", "tunnel_tcp_stack", "rsd", "core_device_proxy"] +xctest = [ + "dvt", + "installation_proxy", + "afc", + "dep:uuid", + "dep:ns-keyed-archive", + "tunnel_tcp_stack", + "rsd", + "core_device_proxy", +] springboardservices = [] misagent = [] mobile_image_mounter = ["dep:sha2"] diff --git a/idevice/src/usbmuxd/des.rs b/idevice/src/usbmuxd/des.rs index adf672c5..b1216ce8 100644 --- a/idevice/src/usbmuxd/des.rs +++ b/idevice/src/usbmuxd/des.rs @@ -5,6 +5,11 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use serde::Deserialize; use tracing::{debug, warn}; +#[cfg(not(windows))] +use libc::{AF_INET, AF_INET6}; +#[cfg(windows)] +use windows_sys::Win32::Networking::WinSock::{AF_INET, AF_INET6}; + use crate::{ IdeviceError, usbmuxd::{Connection, UsbmuxdDevice}, @@ -55,14 +60,14 @@ impl TryFrom for UsbmuxdDevice { )); } - match addr[0] as libc::c_int { - libc::AF_INET => { + match addr[0] as _ { + AF_INET => { // IPv4 Connection::Network(IpAddr::V4(Ipv4Addr::new( addr[4], addr[5], addr[6], addr[7], ))) } - libc::AF_INET6 => { + AF_INET6 => { // IPv6 if addr.len() < 24 { warn!("IPv6 address is less than 24 bytes"); @@ -91,7 +96,7 @@ impl TryFrom for UsbmuxdDevice { .into(), )); } - if addr[1] as libc::c_int == libc::AF_INET6 { + if addr[1] == AF_INET6 as u8 { // IPv6 address starts at offset 8 in sockaddr_in6 Connection::Network(IpAddr::V6(Ipv6Addr::new( u16::from_be_bytes([addr[8], addr[9]]), From 50737fc409f23dbb2ee10ae1b99f3b21b1106bd2 Mon Sep 17 00:00:00 2001 From: Abdullah Al-Banna Date: Sat, 18 Apr 2026 19:07:24 +0300 Subject: [PATCH 3/3] remove windows_sys --- Cargo.lock | 1 - idevice/Cargo.toml | 4 +--- idevice/src/usbmuxd/des.rs | 5 ++++- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 809e9c72..6631e966 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1309,7 +1309,6 @@ dependencies = [ "tracing", "tun-rs", "uuid", - "windows-sys 0.61.2", "x25519-dalek", "x509-cert", ] diff --git a/idevice/Cargo.toml b/idevice/Cargo.toml index 63300060..f63292e5 100644 --- a/idevice/Cargo.toml +++ b/idevice/Cargo.toml @@ -69,11 +69,9 @@ obfstr = { version = "0.4", optional = true } async_zip = { version = "0.0.18", optional = true } +[target.'cfg(not(windows))'.dependencies] libc = "0.2" -[target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.61", features = ["Win32_Networking_WinSock"] } - [dev-dependencies] tokio = { version = "1.43", features = ["full"] } tun-rs = { version = "2.0.8", features = ["async_tokio"] } diff --git a/idevice/src/usbmuxd/des.rs b/idevice/src/usbmuxd/des.rs index b1216ce8..6e89ed81 100644 --- a/idevice/src/usbmuxd/des.rs +++ b/idevice/src/usbmuxd/des.rs @@ -7,8 +7,11 @@ use tracing::{debug, warn}; #[cfg(not(windows))] use libc::{AF_INET, AF_INET6}; + +#[cfg(windows)] +const AF_INET: i32 = 2; #[cfg(windows)] -use windows_sys::Win32::Networking::WinSock::{AF_INET, AF_INET6}; +const AF_INET6: i32 = 23; use crate::{ IdeviceError,