From 71a223f669ae13b194ed77a0856bbb03560bad68 Mon Sep 17 00:00:00 2001 From: Jackson Coxson Date: Thu, 3 Jul 2025 09:43:00 -0600 Subject: [PATCH] Allow for domain lookups for get_all --- idevice/src/services/lockdown.rs | 19 +++-- tools/Cargo.toml | 4 + tools/src/ideviceinfo.rs | 2 +- tools/src/lockdown.rs | 131 +++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 8 deletions(-) create mode 100644 tools/src/lockdown.rs diff --git a/idevice/src/services/lockdown.rs b/idevice/src/services/lockdown.rs index 883a738..f25686a 100644 --- a/idevice/src/services/lockdown.rs +++ b/idevice/src/services/lockdown.rs @@ -127,13 +127,18 @@ impl LockdownClient { /// println!("{}: {:?}", key, value); /// } /// ``` - pub async fn get_all_values(&mut self) -> Result { - let req = LockdownRequest { - label: self.idevice.label.clone(), - key: None, - request: "GetValue".to_string(), - }; - let message = plist::to_value(&req)?; + pub async fn get_all_values( + &mut self, + domain: Option, + ) -> Result { + let mut request = plist::Dictionary::new(); + request.insert("Label".into(), self.idevice.label.clone().into()); + request.insert("Request".into(), "GetValue".into()); + if let Some(domain) = domain { + request.insert("Domain".into(), domain.into()); + } + + let message = plist::to_value(&request)?; self.idevice.send_plist(message).await?; let message: plist::Dictionary = self.idevice.read_plist().await?; match message.get("Value") { diff --git a/tools/Cargo.toml b/tools/Cargo.toml index f28d262..ec26a97 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -81,6 +81,10 @@ path = "src/syslog_relay.rs" name = "os_trace_relay" path = "src/os_trace_relay.rs" +[[bin]] +name = "lockdown" +path = "src/lockdown.rs" + [dependencies] idevice = { path = "../idevice", features = ["full"] } tokio = { version = "1.43", features = ["io-util", "macros", "time", "full"] } diff --git a/tools/src/ideviceinfo.rs b/tools/src/ideviceinfo.rs index d882ebd..948a8c1 100644 --- a/tools/src/ideviceinfo.rs +++ b/tools/src/ideviceinfo.rs @@ -82,5 +82,5 @@ async fn main() { .await ); println!("{:?}", lockdown_client.idevice.get_type().await.unwrap()); - println!("{:#?}", lockdown_client.get_all_values().await); + println!("{:#?}", lockdown_client.get_all_values(None).await); } diff --git a/tools/src/lockdown.rs b/tools/src/lockdown.rs new file mode 100644 index 0000000..00f3def --- /dev/null +++ b/tools/src/lockdown.rs @@ -0,0 +1,131 @@ +// Jackson Coxson + +use clap::{arg, Arg, Command}; +use idevice::{lockdown::LockdownClient, pretty_print_plist, IdeviceService}; +use plist::Value; + +mod common; + +#[tokio::main] +async fn main() { + env_logger::init(); + + let matches = Command::new("lockdown") + .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), + ) + .subcommand( + Command::new("get") + .about("Gets a value") + .arg(arg!(-v --value "the value to get").required(true)) + .arg(arg!(-d --domain "the domain to get in").required(false)), + ) + .subcommand( + Command::new("get_all") + .about("Gets all") + .arg(arg!(-d --domain "the domain to get in").required(false)), + ) + .subcommand( + Command::new("set") + .about("Sets a lockdown value") + .arg(arg!(-k --key "the key to set").required(true)) + .arg(arg!(-v --value "the value to set the key to").required(true)) + .arg(arg!(-d --domain "the domain to get in").required(false)), + ) + .get_matches(); + + if matches.get_flag("about") { + println!("lockdown - query and manage values 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; + } + }; + + let mut lockdown_client = LockdownClient::connect(&*provider) + .await + .expect("Unable to connect to lockdown"); + + lockdown_client + .start_session(&provider.get_pairing_file().await.expect("no pairing file")) + .await + .expect("no session"); + + match matches.subcommand() { + Some(("get", sub_m)) => { + let key = sub_m.get_one::("value").unwrap(); + let domain = sub_m.get_one::("domain").cloned(); + + match lockdown_client.get_value(key, domain).await { + Ok(value) => { + println!("{}", pretty_print_plist(&value)); + } + Err(e) => { + eprintln!("Error getting value: {e}"); + } + } + } + Some(("get_all", sub_m)) => { + let domain = sub_m.get_one::("domain").cloned(); + + match lockdown_client.get_all_values(domain).await { + Ok(value) => { + println!("{}", pretty_print_plist(&plist::Value::Dictionary(value))); + } + Err(e) => { + eprintln!("Error getting value: {e}"); + } + } + } + + Some(("set", sub_m)) => { + let key = sub_m.get_one::("key").unwrap(); + let value_str = sub_m.get_one::("value").unwrap(); + let domain = sub_m.get_one::("domain").cloned(); + + let value = Value::String(value_str.clone()); + + match lockdown_client.set_value(key, value, domain).await { + Ok(()) => println!("Successfully set"), + Err(e) => eprintln!("Error setting value: {e}"), + } + } + + _ => { + eprintln!("No subcommand provided. Try `--help` for usage."); + } + } +}