From 235de5dab94a7ecf1ec2b3e7eb495a2dd667a677 Mon Sep 17 00:00:00 2001 From: Jackson Coxson Date: Sat, 1 Feb 2025 15:49:23 -0700 Subject: [PATCH] Implement new library for tools --- Cargo.lock | 34 ++++++++ tools/Cargo.toml | 1 + tools/src/common.rs | 65 ++++++++++++++ tools/src/core_device_proxy_tun.rs | 124 ++++++++++++--------------- tools/src/heartbeat_client.rs | 128 ++++++++++++---------------- tools/src/ideviceinfo.rs | 119 +++++++++++++------------- tools/src/instproxy.rs | 128 ++++++++++++---------------- tools/src/mounter.rs | 131 ++++++++++++----------------- 8 files changed, 378 insertions(+), 352 deletions(-) create mode 100644 tools/src/common.rs diff --git a/Cargo.lock b/Cargo.lock index 6801b03..e830342 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -225,6 +225,33 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "clap" +version = "4.5.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + [[package]] name = "colorchoice" version = "1.0.3" @@ -679,6 +706,7 @@ dependencies = [ name = "idevice-tools" version = "0.1.0" dependencies = [ + "clap", "env_logger", "idevice", "log", @@ -1229,6 +1257,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.6.1" diff --git a/tools/Cargo.toml b/tools/Cargo.toml index 30eacbf..fc8cbd4 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -42,3 +42,4 @@ env_logger = { version = "0.11" } tun-rs = { version = "1.5", features = ["async"] } sha2 = { version = "0.10" } ureq = { version = "3" } +clap = { version = "4.5" } diff --git a/tools/src/common.rs b/tools/src/common.rs new file mode 100644 index 0000000..82575c3 --- /dev/null +++ b/tools/src/common.rs @@ -0,0 +1,65 @@ +// Jackson Coxson +// Common functions between tools + +use std::{net::IpAddr, str::FromStr}; + +use idevice::{ + pairing_file::PairingFile, + provider::{IdeviceProvider, TcpProvider}, + usbmuxd::{UsbmuxdAddr, UsbmuxdConnection}, +}; + +pub async fn get_provider( + udid: Option<&String>, + host: Option<&String>, + pairing_file: Option<&String>, + label: &str, +) -> Result, String> { + let provider: Box = if udid.is_some() { + let udid = udid.unwrap(); + let mut usbmuxd = UsbmuxdConnection::default() + .await + .expect("Unable to connect to usbmxud"); + let dev = match usbmuxd.get_device(udid).await { + Ok(d) => d, + Err(e) => { + return Err(format!("Device not found: {e:?}")); + } + }; + Box::new(dev.to_provider(UsbmuxdAddr::default(), 0, label)) + } else if host.is_some() && pairing_file.is_some() { + let host = match IpAddr::from_str(host.unwrap()) { + Ok(h) => h, + Err(e) => { + return Err(format!("Invalid host: {e:?}")); + } + }; + let pairing_file = match PairingFile::read_from_file(pairing_file.unwrap()) { + Ok(p) => p, + Err(e) => { + return Err(format!("Unable to read pairing file: {e:?}")); + } + }; + + Box::new(TcpProvider { + addr: host, + pairing_file, + label: "ideviceinfo-jkcoxson".to_string(), + }) + } else { + let mut usbmuxd = UsbmuxdConnection::default() + .await + .expect("Unable to connect to usbmxud"); + let devs = match usbmuxd.get_devices().await { + Ok(d) => d, + Err(e) => { + return Err(format!("Unable to get devices from usbmuxd: {e:?}")); + } + }; + if devs.is_empty() { + return Err("No devices connected!".to_string()); + } + Box::new(devs[0].to_provider(UsbmuxdAddr::default(), 0, label)) + }; + Ok(provider) +} diff --git a/tools/src/core_device_proxy_tun.rs b/tools/src/core_device_proxy_tun.rs index 41924da..ba1e654 100644 --- a/tools/src/core_device_proxy_tun.rs +++ b/tools/src/core_device_proxy_tun.rs @@ -1,92 +1,74 @@ // Jackson Coxson +use clap::{Arg, Command}; use idevice::{ core_device_proxy::{self}, - lockdownd::{self, LockdowndClient}, - pairing_file::PairingFile, - Idevice, + IdeviceService, }; use tun_rs::AbstractDevice; -use std::{ - net::{Ipv4Addr, SocketAddrV4}, - str::FromStr, -}; +mod common; #[tokio::main] async fn main() { env_logger::init(); - let mut host = None; - let mut pairing_file = None; + let matches = Command::new("core_device_proxy_tun") + .about("Start a tunnel") + .arg( + Arg::new("host") + .long("host") + .value_name("HOST") + .help("IP address of the device"), + ) + .arg( + Arg::new("pairing_file") + .long("pairing-file") + .value_name("PATH") + .help("Path to the pairing file"), + ) + .arg( + Arg::new("udid") + .value_name("UDID") + .help("UDID of the device (overrides host/pairing file)") + .index(1), + ) + .arg( + Arg::new("about") + .long("about") + .help("Show about information") + .action(clap::ArgAction::SetTrue), + ) + .arg( + Arg::new("help") + .short('h') + .long("help") + .help("Show this help message") + .action(clap::ArgAction::SetTrue), + ) + .get_matches(); - // Loop through args - let mut i = 0; - while i < std::env::args().len() { - match std::env::args().nth(i).unwrap().as_str() { - "--host" => { - host = Some(std::env::args().nth(i + 1).unwrap().to_string()); - i += 2; - } - "--pairing-file" => { - pairing_file = Some(std::env::args().nth(i + 1).unwrap().to_string()); - i += 2; - } - "-h" | "--help" => { - println!("core_device_proxy_tun - start a tunnel"); - println!("Usage:"); - println!(" core_device_proxy_tun [options]"); - println!("Options:"); - println!(" --host "); - println!(" --pairing_file "); - println!(" -h, --help"); - println!(" --about"); - println!("\n\nSet RUST_LOG to info, debug, warn, error, or trace to see more logs. Default is error."); - std::process::exit(0); - } - "--about" => { - println!("ideviceinfo - get information from the idevice. Reimplementation of libimobiledevice's binary."); - println!("Copyright (c) 2025 Jackson Coxson"); - } - _ => { - i += 1; - } - } - } - if host.is_none() { - println!("Invalid arguments! Pass the IP of the device with --host"); + if matches.get_flag("about") { + println!("core_device_proxy - Start a lockdown tunnel on the device"); + println!("Copyright (c) 2025 Jackson Coxson"); return; } - if pairing_file.is_none() { - println!("Invalid arguments! Pass the path the the pairing file with --pairing-file"); - return; - } - let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap(); - let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT); - let socket = tokio::net::TcpStream::connect(socket).await.unwrap(); - let socket = Box::new(socket); - let idevice = Idevice::new(socket, "heartbeat_client"); + let udid = matches.get_one::("udid"); + let host = matches.get_one::("host"); + let pairing_file = matches.get_one::("pairing_file"); - let p = PairingFile::read_from_file(pairing_file.as_ref().unwrap()).unwrap(); + let provider = + match common::get_provider(udid, host, pairing_file, "core_device_proxy-jkcoxson").await { + Ok(p) => p, + Err(e) => { + eprintln!("{e}"); + return; + } + }; - let mut lockdown_client = LockdowndClient { idevice }; - lockdown_client.start_session(&p).await.unwrap(); - - let (port, _) = lockdown_client - .start_service(core_device_proxy::SERVCE_NAME) + let mut tun_proxy = core_device_proxy::CoreDeviceProxy::connect(&*provider) .await - .unwrap(); - - let socket = SocketAddrV4::new(ip, port); - let socket = tokio::net::TcpStream::connect(socket).await.unwrap(); - let socket = Box::new(socket); - let mut idevice = Idevice::new(socket, "core_device_proxy_tun"); - - let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap(); - - idevice.start_session(&p).await.unwrap(); - - let mut tun_proxy = core_device_proxy::CoreDeviceProxy::new(idevice); + .expect("Unable to connect"); let response = tun_proxy.establish_tunnel().await.unwrap(); let dev = tun_rs::create(&tun_rs::Configuration::default()).unwrap(); diff --git a/tools/src/heartbeat_client.rs b/tools/src/heartbeat_client.rs index d119c88..4ed859a 100644 --- a/tools/src/heartbeat_client.rs +++ b/tools/src/heartbeat_client.rs @@ -1,92 +1,70 @@ // Jackson Coxson // Heartbeat client -use idevice::{ - heartbeat::HeartbeatClient, - lockdownd::{self, LockdowndClient}, - pairing_file::PairingFile, - Idevice, -}; +use clap::{Arg, Command}; +use idevice::{heartbeat::HeartbeatClient, IdeviceService}; -use std::{ - net::{Ipv4Addr, SocketAddrV4}, - str::FromStr, -}; +mod common; #[tokio::main] async fn main() { env_logger::init(); - let mut host = None; - let mut pairing_file = None; + let matches = Command::new("core_device_proxy_tun") + .about("Start a tunnel") + .arg( + Arg::new("host") + .long("host") + .value_name("HOST") + .help("IP address of the device"), + ) + .arg( + Arg::new("pairing_file") + .long("pairing-file") + .value_name("PATH") + .help("Path to the pairing file"), + ) + .arg( + Arg::new("udid") + .value_name("UDID") + .help("UDID of the device (overrides host/pairing file)") + .index(1), + ) + .arg( + Arg::new("about") + .long("about") + .help("Show about information") + .action(clap::ArgAction::SetTrue), + ) + .arg( + Arg::new("help") + .short('h') + .long("help") + .help("Show this help message") + .action(clap::ArgAction::SetTrue), + ) + .get_matches(); - // Loop through args - let mut i = 0; - while i < std::env::args().len() { - match std::env::args().nth(i).unwrap().as_str() { - "--host" => { - host = Some(std::env::args().nth(i + 1).unwrap().to_string()); - i += 2; - } - "--pairing-file" => { - pairing_file = Some(std::env::args().nth(i + 1).unwrap().to_string()); - i += 2; - } - "-h" | "--help" => { - println!("ideviceinfo - get information from the idevice"); - println!("Usage:"); - println!(" ideviceinfo [options]"); - println!("Options:"); - println!(" --host "); - println!(" --pairing_file "); - println!(" -h, --help"); - println!(" --about"); - println!("\n\nSet RUST_LOG to info, debug, warn, error, or trace to see more logs. Default is error."); - std::process::exit(0); - } - "--about" => { - println!("ideviceinfo - get information from the idevice. Reimplementation of libimobiledevice's binary."); - println!("Copyright (c) 2025 Jackson Coxson"); - } - _ => { - i += 1; - } - } - } - if host.is_none() { - println!("Invalid arguments! Pass the IP of the device with --host"); + if matches.get_flag("about") { + println!("heartbeat_client - heartbeat a device"); + println!("Copyright (c) 2025 Jackson Coxson"); return; } - if pairing_file.is_none() { - println!("Invalid arguments! Pass the path the the pairing file with --pairing-file"); - return; - } - let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap(); - let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT); - let socket = tokio::net::TcpStream::connect(socket).await.unwrap(); - let socket = Box::new(socket); - let idevice = Idevice::new(socket, "heartbeat_client"); + let udid = matches.get_one::("udid"); + let host = matches.get_one::("host"); + let pairing_file = matches.get_one::("pairing_file"); - let p = PairingFile::read_from_file(pairing_file.as_ref().unwrap()).unwrap(); - - let mut lockdown_client = LockdowndClient { idevice }; - lockdown_client.start_session(&p).await.unwrap(); - - let (port, _) = lockdown_client - .start_service("com.apple.mobile.heartbeat") + let provider = + match common::get_provider(udid, host, pairing_file, "heartbeat_client-jkcoxson").await { + Ok(p) => p, + Err(e) => { + eprintln!("{e}"); + return; + } + }; + let mut heartbeat_client = HeartbeatClient::connect(&*provider) .await - .unwrap(); - - let socket = SocketAddrV4::new(ip, port); - let socket = tokio::net::TcpStream::connect(socket).await.unwrap(); - let socket = Box::new(socket); - let mut idevice = Idevice::new(socket, "heartbeat_client"); - - let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap(); - - idevice.start_session(&p).await.unwrap(); - - let mut heartbeat_client = HeartbeatClient { idevice }; + .expect("Unable to connect to heartbeat"); let mut interval = 15; loop { diff --git a/tools/src/ideviceinfo.rs b/tools/src/ideviceinfo.rs index f76c119..c2ed220 100644 --- a/tools/src/ideviceinfo.rs +++ b/tools/src/ideviceinfo.rs @@ -1,72 +1,77 @@ // Jackson Coxson // idevice Rust implementation of libimobiledevice's ideviceinfo -use std::{ - net::{Ipv4Addr, SocketAddrV4}, - str::FromStr, -}; +use clap::{Arg, Command}; +use idevice::{lockdownd::LockdowndClient, pairing_file::PairingFile, IdeviceService}; -use idevice::{ - lockdownd::{self, LockdowndClient}, - pairing_file::PairingFile, - Idevice, -}; +mod common; #[tokio::main] async fn main() { env_logger::init(); - let mut host = None; - let mut pairing_file = None; - // Loop through args - let mut i = 0; - while i < std::env::args().len() { - match std::env::args().nth(i).unwrap().as_str() { - "--host" => { - host = Some(std::env::args().nth(i + 1).unwrap().to_string()); - i += 2; - } - "--pairing-file" => { - pairing_file = Some(std::env::args().nth(i + 1).unwrap().to_string()); - i += 2; - } - "-h" | "--help" => { - println!("ideviceinfo - get information from the idevice"); - println!("Usage:"); - println!(" ideviceinfo [options]"); - println!("Options:"); - println!(" --host "); - println!(" --pairing_file "); - println!(" -h, --help"); - println!(" --about"); - println!("\n\nSet RUST_LOG to info, debug, warn, error, or trace to see more logs. Default is error."); - std::process::exit(0); - } - "--about" => { - println!("ideviceinfo - get information from the idevice. Reimplementation of libimobiledevice's binary."); - println!("Copyright (c) 2025 Jackson Coxson"); - } - _ => { - i += 1; + let matches = Command::new("core_device_proxy_tun") + .about("Start a tunnel") + .arg( + Arg::new("host") + .long("host") + .value_name("HOST") + .help("IP address of the device"), + ) + .arg( + Arg::new("pairing_file") + .long("pairing-file") + .value_name("PATH") + .help("Path to the pairing file"), + ) + .arg( + Arg::new("udid") + .value_name("UDID") + .help("UDID of the device (overrides host/pairing file)") + .index(1), + ) + .arg( + Arg::new("about") + .long("about") + .help("Show about information") + .action(clap::ArgAction::SetTrue), + ) + .arg( + Arg::new("help") + .short('h') + .long("help") + .help("Show this help message") + .action(clap::ArgAction::SetTrue), + ) + .get_matches(); + + if matches.get_flag("about") { + println!("ideviceinfo - get information from the idevice. Reimplementation of libimobiledevice's binary."); + println!("Copyright (c) 2025 Jackson Coxson"); + return; + } + + let udid = matches.get_one::("udid"); + let host = matches.get_one::("host"); + let pairing_file = matches.get_one::("pairing_file"); + + let provider = + match common::get_provider(udid, host, pairing_file, "ideviceinfo-jkcoxson").await { + Ok(p) => p, + Err(e) => { + eprintln!("{e}"); + return; } + }; + + let mut lockdown_client = match LockdowndClient::connect(&*provider).await { + Ok(l) => l, + Err(e) => { + eprintln!("Unable to connect to lockdown: {e:?}"); + return; } - } - if host.is_none() { - println!("Invalid arguments! Pass the IP of the device with --host"); - return; - } - if pairing_file.is_none() { - println!("Invalid arguments! Pass the path the the pairing file with --pairing-file"); - return; - } - let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap(); - let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT); + }; - let socket = tokio::net::TcpStream::connect(socket).await.unwrap(); - let socket = Box::new(socket); - let idevice = Idevice::new(socket, "ideviceinfo-jkcoxson"); - - let mut lockdown_client = LockdowndClient::new(idevice); println!("{:?}", lockdown_client.get_value("ProductVersion").await); let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap(); diff --git a/tools/src/instproxy.rs b/tools/src/instproxy.rs index 556acb1..0f8339f 100644 --- a/tools/src/instproxy.rs +++ b/tools/src/instproxy.rs @@ -1,92 +1,72 @@ // Jackson Coxson // Just lists apps for now -use idevice::{ - installation_proxy::InstallationProxyClient, - lockdownd::{self, LockdowndClient}, - pairing_file::PairingFile, - Idevice, -}; +use clap::{Arg, Command}; +use idevice::{installation_proxy::InstallationProxyClient, IdeviceService}; -use std::{ - net::{Ipv4Addr, SocketAddrV4}, - str::FromStr, -}; +mod common; #[tokio::main] async fn main() { env_logger::init(); - let mut host = None; - let mut pairing_file = None; - // Loop through args - let mut i = 0; - while i < std::env::args().len() { - match std::env::args().nth(i).unwrap().as_str() { - "--host" => { - host = Some(std::env::args().nth(i + 1).unwrap().to_string()); - i += 2; - } - "--pairing-file" => { - pairing_file = Some(std::env::args().nth(i + 1).unwrap().to_string()); - i += 2; - } - "-h" | "--help" => { - println!("ideviceinfo - get information from the idevice"); - println!("Usage:"); - println!(" ideviceinfo [options]"); - println!("Options:"); - println!(" --host "); - println!(" --pairing_file "); - println!(" -h, --help"); - println!(" --about"); - println!("\n\nSet RUST_LOG to info, debug, warn, error, or trace to see more logs. Default is error."); - std::process::exit(0); - } - "--about" => { - println!("ideviceinfo - get information from the idevice. Reimplementation of libimobiledevice's binary."); - println!("Copyright (c) 2025 Jackson Coxson"); - } - _ => { - i += 1; - } - } - } - if host.is_none() { - println!("Invalid arguments! Pass the IP of the device with --host"); + let matches = Command::new("core_device_proxy_tun") + .about("Start a tunnel") + .arg( + Arg::new("host") + .long("host") + .value_name("HOST") + .help("IP address of the device"), + ) + .arg( + Arg::new("pairing_file") + .long("pairing-file") + .value_name("PATH") + .help("Path to the pairing file"), + ) + .arg( + Arg::new("udid") + .value_name("UDID") + .help("UDID of the device (overrides host/pairing file)") + .index(1), + ) + .arg( + Arg::new("about") + .long("about") + .help("Show about information") + .action(clap::ArgAction::SetTrue), + ) + .arg( + Arg::new("help") + .short('h') + .long("help") + .help("Show this help message") + .action(clap::ArgAction::SetTrue), + ) + .get_matches(); + + if matches.get_flag("about") { + println!("instproxy - query and manage apps installed on a device. Reimplementation of libimobiledevice's binary."); + println!("Copyright (c) 2025 Jackson Coxson"); return; } - if pairing_file.is_none() { - println!("Invalid arguments! Pass the path the the pairing file with --pairing-file"); - return; - } - let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap(); - let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT); - let socket = tokio::net::TcpStream::connect(socket).await.unwrap(); - let socket = Box::new(socket); - let idevice = Idevice::new(socket, "heartbeat_client"); + let udid = matches.get_one::("udid"); + let host = matches.get_one::("host"); + let pairing_file = matches.get_one::("pairing_file"); - let p = PairingFile::read_from_file(pairing_file.as_ref().unwrap()).unwrap(); + let provider = + match common::get_provider(udid, host, pairing_file, "ideviceinfo-jkcoxson").await { + Ok(p) => p, + Err(e) => { + eprintln!("{e}"); + return; + } + }; - let mut lockdown_client = LockdowndClient { idevice }; - lockdown_client.start_session(&p).await.unwrap(); - - let (port, _) = lockdown_client - .start_service("com.apple.mobile.installation_proxy") + let mut instproxy_client = InstallationProxyClient::connect(&*provider) .await - .unwrap(); - - let socket = SocketAddrV4::new(ip, port); - let socket = tokio::net::TcpStream::connect(socket).await.unwrap(); - let socket = Box::new(socket); - let mut idevice = Idevice::new(socket, "instproxy-client"); - - let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap(); - - idevice.start_session(&p).await.unwrap(); - - let mut instproxy_client = InstallationProxyClient::new(idevice); + .expect("Unable to connect to instproxy"); let apps = instproxy_client.get_apps(None, None).await.unwrap(); for app in apps.keys() { println!("{app}"); diff --git a/tools/src/mounter.rs b/tools/src/mounter.rs index 72aeea1..1dc7642 100644 --- a/tools/src/mounter.rs +++ b/tools/src/mounter.rs @@ -1,94 +1,75 @@ // Jackson Coxson // Just lists apps for now -use idevice::{ - lockdownd::{self, LockdowndClient}, - mounter::ImageMounter, - pairing_file::PairingFile, - Idevice, -}; +use clap::{Arg, Command}; +use idevice::{mounter::ImageMounter, IdeviceService}; use sha2::{Digest, Sha384}; -use std::{ - net::{Ipv4Addr, SocketAddrV4}, - str::FromStr, -}; +mod common; #[tokio::main] async fn main() { env_logger::init(); - let mut host = None; - let mut pairing_file = None; - // Loop through args - let mut i = 0; - while i < std::env::args().len() { - match std::env::args().nth(i).unwrap().as_str() { - "--host" => { - host = Some(std::env::args().nth(i + 1).unwrap().to_string()); - i += 2; - } - "--pairing-file" => { - pairing_file = Some(std::env::args().nth(i + 1).unwrap().to_string()); - i += 2; - } - "-h" | "--help" => { - println!("ideviceinfo - get information from the idevice"); - println!("Usage:"); - println!(" ideviceinfo [options]"); - println!("Options:"); - println!(" --host "); - println!(" --pairing_file "); - println!(" -h, --help"); - println!(" --about"); - println!("\n\nSet RUST_LOG to info, debug, warn, error, or trace to see more logs. Default is error."); + let matches = Command::new("core_device_proxy_tun") + .about("Start a tunnel") + .arg( + Arg::new("host") + .long("host") + .value_name("HOST") + .help("IP address of the device"), + ) + .arg( + Arg::new("pairing_file") + .long("pairing-file") + .value_name("PATH") + .help("Path to the pairing file"), + ) + .arg( + Arg::new("udid") + .value_name("UDID") + .help("UDID of the device (overrides host/pairing file)") + .index(1), + ) + .arg( + Arg::new("about") + .long("about") + .help("Show about information") + .action(clap::ArgAction::SetTrue), + ) + .arg( + Arg::new("help") + .short('h') + .long("help") + .help("Show this help message") + .action(clap::ArgAction::SetTrue), + ) + .get_matches(); + + if matches.get_flag("about") { + println!("mounter - query and manage images mounted on a device. Reimplementation of libimobiledevice's binary."); + println!("Copyright (c) 2025 Jackson Coxson"); + return; + } + + let udid = matches.get_one::("udid"); + let host = matches.get_one::("host"); + let pairing_file = matches.get_one::("pairing_file"); + + let provider = + match common::get_provider(udid, host, pairing_file, "ideviceinfo-jkcoxson").await { + Ok(p) => p, + Err(e) => { + eprintln!("{e}"); return; } - "--about" => { - println!("mounter - mount developer disk images on the device. Reimplementation of pymobiledevice3's program."); - println!("Copyright (c) 2025 Jackson Coxson"); - } - _ => { - i += 1; - } - } - } - if host.is_none() { - println!("Invalid arguments! Pass the IP of the device with --host"); - return; - } - if pairing_file.is_none() { - println!("Invalid arguments! Pass the path the the pairing file with --pairing-file"); - return; - } - let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap(); - let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT); + }; - let socket = tokio::net::TcpStream::connect(socket).await.unwrap(); - let socket = Box::new(socket); - let idevice = Idevice::new(socket, "mounter_client"); - - let p = PairingFile::read_from_file(pairing_file.as_ref().unwrap()).unwrap(); - - let mut lockdown_client = LockdowndClient { idevice }; - lockdown_client.start_session(&p).await.unwrap(); - - let (port, _) = lockdown_client - .start_service("com.apple.mobile.mobile_image_mounter") + let mut mounter_client = ImageMounter::connect(&*provider) .await - .unwrap(); + .expect("Unable to connect to image mounter"); - let socket = SocketAddrV4::new(ip, port); - let socket = tokio::net::TcpStream::connect(socket).await.unwrap(); - let socket = Box::new(socket); - let mut idevice = Idevice::new(socket, "mounter_client"); - - let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap(); - - idevice.start_session(&p).await.unwrap(); - - let mut mounter_client = ImageMounter::new(idevice); let images = mounter_client.copy_devices().await.unwrap(); println!("Images: {images:#?}");