From e8225b03bc6307d22930e2a3be1c8b1d364fc359 Mon Sep 17 00:00:00 2001 From: Jackson Coxson Date: Thu, 13 Mar 2025 13:53:03 -0600 Subject: [PATCH] Use smoltcp as a software TUN manager (broken) --- Cargo.lock | 184 +++++++++++- idevice/Cargo.toml | 7 +- idevice/src/core_device_proxy/device.rs | 141 +++++++++ idevice/src/core_device_proxy/interface.rs | 277 ++++++++++++++++++ .../mod.rs} | 37 ++- idevice/src/lib.rs | 17 +- tools/src/core_device_proxy_tun.rs | 84 ++++-- 7 files changed, 680 insertions(+), 67 deletions(-) create mode 100644 idevice/src/core_device_proxy/device.rs create mode 100644 idevice/src/core_device_proxy/interface.rs rename idevice/src/{core_device_proxy.rs => core_device_proxy/mod.rs} (86%) diff --git a/Cargo.lock b/Cargo.lock index 291808a..f05014b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,6 +138,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.8.0" @@ -178,6 +184,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder_slice" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b294e30387378958e8bf8f4242131b930ea615ff81e8cac2440cea0a6013190" +dependencies = [ + "byteorder", +] + [[package]] name = "bytes" version = "1.9.0" @@ -365,6 +380,38 @@ dependencies = [ "typenum", ] +[[package]] +name = "defmt" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "defmt-parser" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3" +dependencies = [ + "thiserror 2.0.11", +] + [[package]] name = "deranged" version = "0.3.11" @@ -374,6 +421,17 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive-into-owned" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d94d81e3819a7b06a8638f448bc6339371ca9b6076a99d4a43eece3c4c923" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.10.7" @@ -590,7 +648,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ba121d81ab5ea05b0cd5858516266800bf965531a794f7ac58e3eeb804f364f" dependencies = [ - "bitflags", + "bitflags 2.8.0", "libc", "windows-sys 0.59.0", ] @@ -643,12 +701,31 @@ dependencies = [ "tracing", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -904,12 +981,14 @@ dependencies = [ "log", "ns-keyed-archive", "openssl", + "pcap-file", "plist", "reqwest", "serde", "serde_json", "sha2", - "thiserror", + "smoltcp", + "thiserror 2.0.11", "tokio", "tokio-openssl", "uuid", @@ -1057,6 +1136,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + [[package]] name = "memchr" version = "2.7.4" @@ -1121,7 +1206,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags", + "bitflags 2.8.0", "cfg-if", "cfg_aliases 0.1.1", "libc", @@ -1134,7 +1219,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags", + "bitflags 2.8.0", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -1158,7 +1243,7 @@ checksum = "f237a10fe003123daa55a74b63a0b0a65de1671b2d128711ffe5886891a8f77f" dependencies = [ "clap", "plist", - "thiserror", + "thiserror 2.0.11", ] [[package]] @@ -1188,7 +1273,7 @@ version = "0.10.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" dependencies = [ - "bitflags", + "bitflags 2.8.0", "cfg-if", "foreign-types", "libc", @@ -1265,6 +1350,17 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "pcap-file" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc1f139757b058f9f37b76c48501799d12c9aa0aa4c0d4c980b062ee925d1b2" +dependencies = [ + "byteorder_slice", + "derive-into-owned", + "thiserror 1.0.69", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1319,6 +1415,28 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "proc-macro2" version = "1.0.93" @@ -1352,7 +1470,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags", + "bitflags 2.8.0", ] [[package]] @@ -1455,7 +1573,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys", @@ -1536,7 +1654,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags", + "bitflags 2.8.0", "core-foundation", "core-foundation-sys", "libc", @@ -1638,6 +1756,22 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "smoltcp" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "cfg-if", + "defmt", + "heapless", + "libc", + "log", + "managed", +] + [[package]] name = "socket2" version = "0.5.8" @@ -1720,7 +1854,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags", + "bitflags 2.8.0", "core-foundation", "system-configuration-sys", ] @@ -1749,13 +1883,33 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.11", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -1941,7 +2095,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53141e64197ff7e758b8152615e50bb4a3b18c970738876e7906d31f242c7d6e" dependencies = [ - "bitflags", + "bitflags 2.8.0", "blocking", "byteorder", "bytes", @@ -1956,7 +2110,7 @@ dependencies = [ "mac_address", "nix 0.29.0", "scopeguard", - "thiserror", + "thiserror 2.0.11", "tokio", "windows-sys 0.59.0", "winreg", @@ -2406,7 +2560,7 @@ dependencies = [ "c2rust-bitfields", "libloading", "log", - "thiserror", + "thiserror 2.0.11", "windows-sys 0.59.0", ] @@ -2416,7 +2570,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ - "bitflags", + "bitflags 2.8.0", ] [[package]] diff --git a/idevice/Cargo.toml b/idevice/Cargo.toml index d121bab..0114834 100644 --- a/idevice/Cargo.toml +++ b/idevice/Cargo.toml @@ -32,13 +32,14 @@ json = { version = "0.12", optional = true } byteorder = { version = "1.5", optional = true } reqwest = { version = "0.12", features = ["json"], optional = true } +smoltcp = { version = "0.12", optional = true } +pcap-file = { version = "2.0", optional = true } sha2 = { version = "0.10", optional = true } [dev-dependencies] tokio = { version = "1.43", features = ["fs"] } - [features] core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"] debug_proxy = [] @@ -47,10 +48,11 @@ heartbeat = [] installation_proxy = [] misagent = [] mounter = ["dep:sha2"] -usbmuxd = ["tokio/net"] tcp = ["tokio/net"] +tunnel_tcp_stack = ["dep:smoltcp", "tokio/sync", "dep:pcap-file"] tss = ["dep:uuid", "dep:reqwest"] tunneld = ["dep:serde_json", "dep:json", "dep:reqwest"] +usbmuxd = ["tokio/net"] xpc = [ "tokio/sync", "dep:indexmap", @@ -70,6 +72,7 @@ full = [ "usbmuxd", "xpc", "tcp", + "tunnel_tcp_stack", "tss", "tunneld", ] diff --git a/idevice/src/core_device_proxy/device.rs b/idevice/src/core_device_proxy/device.rs new file mode 100644 index 0000000..94d0ff7 --- /dev/null +++ b/idevice/src/core_device_proxy/device.rs @@ -0,0 +1,141 @@ +// Jackson Coxson + +use log::{debug, warn}; +use pcap_file::pcapng::PcapNgWriter; +use smoltcp::{ + phy::{self, Device, DeviceCapabilities, Medium}, + time::Instant, +}; +use tokio::sync::{ + mpsc::{error::TryRecvError, unbounded_channel, UnboundedReceiver, UnboundedSender}, + oneshot::{channel, Sender}, +}; + +use crate::IdeviceError; + +use super::CoreDeviceProxy; + +pub struct ProxyDevice { + sender: UnboundedSender<(Vec, Sender>)>, + receiver: UnboundedReceiver, IdeviceError>>, + mtu: usize, +} + +impl ProxyDevice { + pub fn new(mut proxy: CoreDeviceProxy) -> Self { + let (sender, mut stack_recv) = unbounded_channel(); + let (stack_send, receiver) = unbounded_channel(); + let mtu = proxy.mtu as usize; + + tokio::task::spawn(async move { + loop { + tokio::select! { + res = proxy.recv() => { + // debug!("stack recv: {res:02X?}"); + if stack_send.send(res).is_err() { + warn!("Interface failed to recv"); + break; + } + } + pkt = stack_recv.recv() => { + let pkt: (Vec, Sender>) = match pkt { + Some(p) => p, + None => { + warn!("Interface sender closed"); + break; + } + }; + + // debug!("stack send: {:02X?}", pkt.0); + pkt.1.send(proxy.send(&pkt.0).await.err()).ok(); + + } + } + } + }); + + Self { + sender, + receiver, + mtu, + } + } +} + +impl Device for ProxyDevice { + type RxToken<'a> = RxToken; + type TxToken<'a> = TxToken; + + fn capabilities(&self) -> DeviceCapabilities { + let mut caps = DeviceCapabilities::default(); + caps.medium = Medium::Ip; + caps.max_transmission_unit = self.mtu; + caps + } + + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + match self.receiver.try_recv() { + Ok(Ok(buffer)) => { + let rx = RxToken { buffer }; + let tx = TxToken { + sender: self.sender.clone(), + }; + Some((rx, tx)) + } + Ok(Err(e)) => { + warn!("Failed to recv message: {e:?}"); + None + } + Err(TryRecvError::Disconnected) => { + warn!("Proxy sender is closed"); + None + } + Err(TryRecvError::Empty) => None, + } + } + + fn transmit(&mut self, _timestamp: Instant) -> Option> { + Some(TxToken { + sender: self.sender.clone(), + }) + } +} + +#[doc(hidden)] +pub struct RxToken { + buffer: Vec, +} + +impl phy::RxToken for RxToken { + fn consume(self, f: F) -> R + where + F: FnOnce(&[u8]) -> R, + { + f(&self.buffer[..]) + } +} + +#[doc(hidden)] +pub struct TxToken { + sender: UnboundedSender<(Vec, Sender>)>, +} + +impl phy::TxToken for TxToken { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let mut buffer = vec![0; len]; + let result = f(&mut buffer); + let (tx, rx) = channel(); + match self.sender.send((buffer, tx)) { + Ok(_) => { + if let Err(e) = rx.blocking_recv() { + warn!("Failed to send to idevice: {e:?}"); + } + } + Err(err) => warn!("Failed to send: {}", err), + } + result + } +} diff --git a/idevice/src/core_device_proxy/interface.rs b/idevice/src/core_device_proxy/interface.rs new file mode 100644 index 0000000..0563a92 --- /dev/null +++ b/idevice/src/core_device_proxy/interface.rs @@ -0,0 +1,277 @@ +// Jackson Coxson + +use std::{ + collections::HashMap, + io::ErrorKind, + net::{IpAddr, Ipv6Addr}, + pin::Pin, + str::FromStr, + task::{Context, Poll}, +}; + +use log::{debug, warn}; +use smoltcp::{ + iface::{Config, Interface, SocketHandle, SocketSet}, + socket::tcp, + time::Instant, + wire::{IpAddress, IpCidr}, +}; +use tokio::{ + io::{self, AsyncRead, AsyncWrite}, + sync::mpsc::{error::TryRecvError, unbounded_channel, UnboundedReceiver, UnboundedSender}, +}; + +use crate::IdeviceError; + +const TCP_BUFFER_SIZE: usize = 1024 * 1024; + +pub struct TunnelInterface { + thread_sender: UnboundedSender, +} + +enum ThreadMessage { + NewTcp((u16, UnboundedSender>, UnboundedReceiver>)), + // left for possible UDP/other in the future?? +} + +struct TunnelSocketHandle { + handle: SocketHandle, + sender: UnboundedSender>, + receiver: UnboundedReceiver>, +} + +#[derive(Debug)] +pub struct Channel { + sender: UnboundedSender>, + receiver: UnboundedReceiver>, +} + +impl TunnelInterface { + pub fn new(proxy: super::CoreDeviceProxy) -> Result { + let ip: IpAddr = match IpAddr::from_str(&proxy.handshake.client_parameters.address) { + Ok(i) => i, + Err(e) => { + warn!("Failed to parse IP as IP address: {e:?}"); + return Err(IdeviceError::UnexpectedResponse); + } + }; + let device_ip: Ipv6Addr = match proxy.handshake.server_address.parse() { + Ok(d) => d, + Err(_) => { + warn!("Device IP isn't parsable as IPv6"); + return Err(IdeviceError::UnexpectedResponse); + } + }; + let mut device = super::device::ProxyDevice::new(proxy); + let config = Config::new(smoltcp::wire::HardwareAddress::Ip); + + // Create the interface + let mut interface = Interface::new(config, &mut device, smoltcp::time::Instant::now()); + + // Add the IPv6 address to the interface + let ip = match ip { + IpAddr::V4(_) => { + warn!("IP was IPv4, not 6"); + return Err(IdeviceError::UnexpectedResponse); + } + IpAddr::V6(ipv6_addr) => ipv6_addr, + }; + interface.update_ip_addrs(|addrs| { + // Add the IPv6 address with a prefix length (e.g., 64 for typical IPv6 subnets) + addrs.push(IpCidr::new(IpAddress::Ipv6(ip), 64)).unwrap(); + }); + + let (thread_sender, mut thread_rx) = unbounded_channel(); + + tokio::task::spawn_blocking(move || { + // based on https://github.com/smoltcp-rs/smoltcp/blob/main/examples/client.rs + let mut sockets = SocketSet::new(vec![]); + let mut handles: HashMap = HashMap::new(); + let mut last_port = 1024; // host to bind and index by + loop { + let timestamp = Instant::now(); + interface.poll(timestamp, &mut device, &mut sockets); + + let mut to_remove = Vec::new(); + let keys: Vec = handles.keys().cloned().collect(); + + for key in keys { + if let Some(handle) = handles.get_mut(&key) { + let socket = sockets.get_mut::(handle.handle); + + if socket.may_recv() { + if let Err(e) = socket.recv(|data| { + if !data.is_empty() && handle.sender.send(data.to_owned()).is_err() + { + warn!("Handle dropped"); + to_remove.push(key); + } + (data.len(), data) + }) { + warn!("Disconnected from socket: {e:?}"); + to_remove.push(key); + } + if socket.can_send() { + match handle.receiver.try_recv() { + Ok(data) => { + let queued = socket.send_slice(&data[..]).unwrap(); + if queued < data.len() { + log::error!("Failed to queue packet for send"); + } + } + Err(TryRecvError::Empty) => {} // wait + Err(TryRecvError::Disconnected) => { + debug!("handle is dropped"); + } + } + } else { + warn!("Can't send!!"); + } + } else if socket.may_send() { + debug!("close"); + socket.close(); + } + } + } + + // Remove the requested threads + for t in to_remove { + if let Some(h) = handles.remove(&t) { + sockets.remove(h.handle); + // When the struct is dropped, the unbounded_channel will close. + // Because the channel is closed, the next time recv or send is called will + // error. + } + } + + match thread_rx.try_recv() { + Ok(msg) => match msg { + ThreadMessage::NewTcp((port, tx, rx)) => { + // Create sockets + let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; TCP_BUFFER_SIZE]); + let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; TCP_BUFFER_SIZE]); + let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); + let handle = sockets.add(tcp_socket); + let socket = sockets.get_mut::(handle); + socket + .connect(interface.context(), (device_ip, port), last_port) + .unwrap(); + handles.insert( + last_port, + TunnelSocketHandle { + handle, + sender: tx, + receiver: rx, + }, + ); + + last_port += 1; + } + }, + Err(TryRecvError::Disconnected) => { + debug!("Thread sender dropped"); + break; + } + Err(TryRecvError::Empty) => { + // noop + } + } + + let poll_delay = interface + .poll_delay(timestamp, &sockets) + .unwrap_or(smoltcp::time::Duration::from_millis(100)) + .millis(); + + std::thread::sleep(std::time::Duration::from_millis(poll_delay)); + } + }); + + Ok(Self { thread_sender }) + } + + pub fn connect_tcp(&mut self, port: u16) -> Result { + let (tx, rx) = unbounded_channel(); + let (rtx, rrx) = unbounded_channel(); + if self + .thread_sender + .send(ThreadMessage::NewTcp((port, rtx, rx))) + .is_err() + { + return Err(IdeviceError::TunnelThreadClosed); + } + + Ok(Channel { + sender: tx, + receiver: rrx, + }) + } +} + +impl Channel { + pub async fn send(&self, data: Vec) -> Result<(), IdeviceError> { + if let Err(e) = self.sender.send(data) { + warn!("Failed to send: {e:?}"); + return Err(IdeviceError::ChannelClosed); + } + Ok(()) + } + + pub async fn recv(&mut self) -> Result, IdeviceError> { + match self.receiver.recv().await { + Some(d) => Ok(d), + None => { + warn!("Failed to recv"); + Err(IdeviceError::ChannelClosed) + } + } + } +} + +impl AsyncRead for Channel { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> Poll> { + match Pin::new(&mut self.receiver).poll_recv(cx) { + Poll::Ready(Some(data)) => { + let len = data.len().min(buf.remaining()); + buf.put_slice(&data[..len]); + Poll::Ready(Ok(())) + } + Poll::Ready(None) => { + warn!("unexpected eof"); + Poll::Ready(Err(io::Error::new( + ErrorKind::UnexpectedEof, + "Channel closed", + ))) + } + Poll::Pending => Poll::Pending, + } + } +} + +impl AsyncWrite for Channel { + fn poll_write( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let data = buf.to_vec(); + match self.sender.send(data) { + Ok(_) => Poll::Ready(Ok(buf.len())), + Err(e) => { + warn!("Failed to send data: {e:?}"); + Poll::Ready(Err(io::Error::new(ErrorKind::BrokenPipe, "Channel closed"))) + } + } + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} diff --git a/idevice/src/core_device_proxy.rs b/idevice/src/core_device_proxy/mod.rs similarity index 86% rename from idevice/src/core_device_proxy.rs rename to idevice/src/core_device_proxy/mod.rs index 7ec4864..de781dc 100644 --- a/idevice/src/core_device_proxy.rs +++ b/idevice/src/core_device_proxy/mod.rs @@ -1,5 +1,10 @@ // Jackson Coxson +#[cfg(feature = "tunnel_tcp_stack")] +mod device; +#[cfg(feature = "tunnel_tcp_stack")] +mod interface; + use crate::{lockdownd::LockdowndClient, Idevice, IdeviceError, IdeviceService}; use byteorder::{BigEndian, WriteBytesExt}; @@ -61,6 +66,7 @@ impl CDTunnelPacket { pub struct CoreDeviceProxy { pub idevice: Idevice, + pub handshake: HandshakeResponse, pub mtu: u32, } @@ -86,7 +92,7 @@ impl IdeviceService for CoreDeviceProxy { .await?; } - Ok(Self::new(idevice)) + Self::new(idevice).await } } @@ -119,14 +125,7 @@ pub struct HandshakeResponse { impl CoreDeviceProxy { const DEFAULT_MTU: u32 = 16000; - pub fn new(idevice: Idevice) -> Self { - Self { - idevice, - mtu: Self::DEFAULT_MTU, - } - } - - pub async fn establish_tunnel(&mut self) -> Result { + pub async fn new(mut idevice: Idevice) -> Result { let req = HandshakeRequest { packet_type: "clientHandshakeRequest".to_string(), mtu: Self::DEFAULT_MTU, @@ -136,11 +135,8 @@ impl CoreDeviceProxy { body: serde_json::to_vec(&req)?, })?; - self.idevice.send_raw(&req).await?; - let recv = self - .idevice - .read_raw(CDTunnelPacket::MAGIC.len() + 2) - .await?; + idevice.send_raw(&req).await?; + let recv = idevice.read_raw(CDTunnelPacket::MAGIC.len() + 2).await?; if recv.len() < CDTunnelPacket::MAGIC.len() + 2 { return Err(IdeviceError::CdtunnelPacketTooShort); @@ -151,10 +147,14 @@ impl CoreDeviceProxy { recv[CDTunnelPacket::MAGIC.len() + 1], ]) as usize; - let recv = self.idevice.read_raw(len).await?; + let recv = idevice.read_raw(len).await?; let res = serde_json::from_slice::(&recv)?; - Ok(res) + Ok(Self { + idevice, + handshake: res, + mtu: Self::DEFAULT_MTU, + }) } pub async fn send(&mut self, data: &[u8]) -> Result<(), IdeviceError> { @@ -165,4 +165,9 @@ impl CoreDeviceProxy { pub async fn recv(&mut self) -> Result, IdeviceError> { self.idevice.read_any(self.mtu).await } + + #[cfg(feature = "tunnel_tcp_stack")] + pub fn create_software_tunnel(self) -> Result { + interface::TunnelInterface::new(self) + } } diff --git a/idevice/src/lib.rs b/idevice/src/lib.rs index 5677801..7cbb8e1 100644 --- a/idevice/src/lib.rs +++ b/idevice/src/lib.rs @@ -29,7 +29,7 @@ mod util; #[cfg(feature = "xpc")] pub mod xpc; -use log::{debug, error}; +use log::{debug, error, trace}; use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; use provider::IdeviceProvider; use std::io::{self, BufWriter}; @@ -144,7 +144,7 @@ impl Idevice { let part_len = message_parts.len() - 1; for (i, part) in message_parts.enumerate() { - debug!("Writing {i}/{part_len}"); + trace!("Writing {i}/{part_len}"); socket.write_all(part).await?; callback(((i, part_len), state.clone())).await; } @@ -231,6 +231,7 @@ impl Idevice { } #[derive(Error, Debug)] +#[non_exhaustive] pub enum IdeviceError { #[error("device socket io failed")] Socket(#[from] io::Error), @@ -316,6 +317,18 @@ pub enum IdeviceError { #[error("disable memory limit failed")] DisableMemoryLimitFailed, + #[cfg(feature = "tunnel_tcp_stack")] + #[error("failed to connect to TCP socket")] + ConnectionError(#[from] smoltcp::socket::tcp::ConnectError), + + #[cfg(feature = "tunnel_tcp_stack")] + #[error("tunnel thread is closed")] + TunnelThreadClosed, + + #[cfg(feature = "tunnel_tcp_stack")] + #[error("channel is closed")] + ChannelClosed, + #[error("not enough bytes, expected {1}, got {0}")] NotEnoughBytes(usize, usize), diff --git a/tools/src/core_device_proxy_tun.rs b/tools/src/core_device_proxy_tun.rs index 54e0322..ac9e85f 100644 --- a/tools/src/core_device_proxy_tun.rs +++ b/tools/src/core_device_proxy_tun.rs @@ -3,6 +3,7 @@ use clap::{Arg, Command}; use idevice::{ core_device_proxy::{self}, + xpc::XPCDevice, IdeviceService, }; use tun_rs::AbstractDevice; @@ -62,38 +63,57 @@ async fn main() { let mut tun_proxy = core_device_proxy::CoreDeviceProxy::connect(&*provider) .await .expect("Unable to connect"); - let response = tun_proxy.establish_tunnel().await.unwrap(); + let rsd_port = tun_proxy.handshake.server_rsd_port; - let dev = tun_rs::create(&tun_rs::Configuration::default()).unwrap(); - dev.add_address_v6(response.client_parameters.address.parse().unwrap(), 32) - .unwrap(); - dev.set_mtu(response.client_parameters.mtu).unwrap(); - dev.set_network_address( - response.client_parameters.address, - response.client_parameters.netmask.parse().unwrap(), - Some(response.server_address.parse().unwrap()), - ) - .unwrap(); + let mut tunnel = tun_proxy.create_software_tunnel().unwrap(); + let channel = tunnel.connect_tcp(rsd_port).unwrap(); + let client = XPCDevice::new(Box::new(channel)).await.expect("no xpc??"); + todo!(); - let async_dev = tun_rs::AsyncDevice::new(dev).unwrap(); - async_dev.enabled(true).unwrap(); - println!("-----------------------------"); - println!("tun device created: {:?}", async_dev.name()); - println!("server address: {}", response.server_address); - println!("rsd port: {}", response.server_rsd_port); - println!("-----------------------------"); - - let mut buf = vec![0; 1500]; - loop { - tokio::select! { - Ok(len) = async_dev.recv(&mut buf) => { - println!("tun pkt: {:?}", &buf[..len]); - tun_proxy.send(&buf[..len]).await.unwrap(); - } - Ok(res) = tun_proxy.recv() => { - println!("dev pkt: {:?}", &res); - async_dev.send(&res).await.unwrap(); - } - } - } + // let dev = tun_rs::create(&tun_rs::Configuration::default()).unwrap(); + // dev.add_address_v6( + // tun_proxy + // .handshake + // .client_parameters + // .address + // .parse() + // .unwrap(), + // 32, + // ) + // .unwrap(); + // dev.set_mtu(tun_proxy.handshake.client_parameters.mtu) + // .unwrap(); + // dev.set_network_address( + // tun_proxy.handshake.client_parameters.address.clone(), + // tun_proxy + // .handshake + // .client_parameters + // .netmask + // .parse() + // .unwrap(), + // Some(tun_proxy.handshake.server_address.parse().unwrap()), + // ) + // .unwrap(); + // + // let async_dev = tun_rs::AsyncDevice::new(dev).unwrap(); + // async_dev.enabled(true).unwrap(); + // println!("-----------------------------"); + // println!("tun device created: {:?}", async_dev.name()); + // println!("server address: {}", tun_proxy.handshake.server_address); + // println!("rsd port: {}", tun_proxy.handshake.server_rsd_port); + // println!("-----------------------------"); + // + // let mut buf = vec![0; 1500]; + // loop { + // tokio::select! { + // Ok(len) = async_dev.recv(&mut buf) => { + // println!("tun pkt: {:?}", &buf[..len]); + // tun_proxy.send(&buf[..len]).await.unwrap(); + // } + // Ok(res) = tun_proxy.recv() => { + // println!("dev pkt: {:?}", &res); + // async_dev.send(&res).await.unwrap(); + // } + // } + // } }