diff --git a/Cargo.lock b/Cargo.lock index 770e934..1f7f736 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2190,9 +2190,9 @@ dependencies = [ [[package]] name = "plist-macro" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ef85ea642f18d31023a2b2c4a3fd823a985b0ab344d1a8fb70d5e122c28544" +checksum = "82c39e020f2d8d361e91a434cfe26bc7c3e21ae54414379816d0110f3f7ca121" dependencies = [ "plist", ] diff --git a/idevice/Cargo.toml b/idevice/Cargo.toml index 8d659bd..9ed7ce4 100644 --- a/idevice/Cargo.toml +++ b/idevice/Cargo.toml @@ -21,7 +21,7 @@ tokio-openssl = { version = "0.6", optional = true } openssl = { version = "0.10", optional = true } plist = { version = "1.8" } -plist-macro = { version = "0.1.3" } +plist-macro = { version = "0.1.6" } serde = { version = "1", features = ["derive"] } ns-keyed-archive = { version = "0.1.4", optional = true } crossfire = { version = "2.1", optional = true } @@ -102,7 +102,12 @@ misagent = [] mobile_image_mounter = ["dep:sha2"] mobileactivationd = ["dep:reqwest"] mobilebackup2 = [] -notification_proxy = ["tokio/macros", "tokio/time", "dep:async-stream", "dep:futures"] +notification_proxy = [ + "tokio/macros", + "tokio/time", + "dep:async-stream", + "dep:futures", +] location_simulation = [] pair = ["chrono/default", "tokio/time", "dep:sha2", "dep:rsa", "dep:x509-cert"] pcapd = [] diff --git a/idevice/src/remote_pairing/mod.rs b/idevice/src/remote_pairing/mod.rs index ed425d2..888e5a3 100644 --- a/idevice/src/remote_pairing/mod.rs +++ b/idevice/src/remote_pairing/mod.rs @@ -1,8 +1,7 @@ //! Remote Pairing -use crate::{IdeviceError, ReadWrite}; +use crate::IdeviceError; -use base64::{Engine as _, engine::general_purpose::STANDARD as B64}; use chacha20poly1305::{ ChaCha20Poly1305, Key, KeyInit, Nonce, aead::{Aead, Payload}, @@ -10,26 +9,28 @@ use chacha20poly1305::{ use ed25519_dalek::Signature; use hkdf::Hkdf; use idevice_srp::{client::SrpClient, groups::G_3072}; +use plist_macro::plist; +use plist_macro::{PlistConvertible, PlistExt}; use rand::RngCore; use rsa::{rand_core::OsRng, signature::SignerMut}; use serde::Serialize; -use serde_json::json; use sha2::Sha512; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tracing::{debug, warn}; use x25519_dalek::{EphemeralSecret, PublicKey as X25519PublicKey}; mod opack; mod rp_pairing_file; +mod socket; mod tlv; // export pub use rp_pairing_file::RpPairingFile; +pub use socket::{RpPairingSocket, RpPairingSocketProvider}; const RPPAIRING_MAGIC: &[u8] = b"RPPairing"; const WIRE_PROTOCOL_VERSION: u8 = 19; -pub struct RemotePairingClient<'a, R: ReadWrite> { +pub struct RemotePairingClient<'a, R: RpPairingSocketProvider> { inner: R, sequence_number: usize, pairing_file: &'a mut RpPairingFile, @@ -39,7 +40,7 @@ pub struct RemotePairingClient<'a, R: ReadWrite> { server_cipher: ChaCha20Poly1305, } -impl<'a, R: ReadWrite> RemotePairingClient<'a, R> { +impl<'a, R: RpPairingSocketProvider> RemotePairingClient<'a, R> { pub fn new(inner: R, sending_host: &str, pairing_file: &'a mut RpPairingFile) -> Self { let hk = Hkdf::::new(None, pairing_file.e_private_key.as_bytes()); let mut okm = [0u8; 32]; @@ -92,22 +93,24 @@ impl<'a, R: ReadWrite> RemotePairingClient<'a, R> { data: x_public_key.to_bytes().to_vec(), }, ]); - let pairing_data = B64.encode(pairing_data); - self.send_pairing_data(json! {{ + let pairing_data = R::serialize_bytes(&pairing_data); + self.send_pairing_data(plist!({ "data": pairing_data, "kind": "verifyManualPairing", "startNewSession": true - }}) + })) .await?; debug!("Waiting for response from verifyManualPairing"); let pairing_data = self.receive_pairing_data().await?; - let pairing_data = match pairing_data.as_str() { - Some(p) => p, - None => return Err(IdeviceError::UnexpectedResponse), + + let data = match R::deserialize_bytes(pairing_data) { + Some(d) => d, + None => { + return Err(IdeviceError::UnexpectedResponse); + } }; - let data = B64.decode(pairing_data)?; let data = tlv::deserialize_tlv8(&data)?; if data @@ -194,23 +197,18 @@ impl<'a, R: ReadWrite> RemotePairingClient<'a, R> { ]; debug!("Waiting for signbuf response"); - self.send_pairing_data(json! {{ - "data": B64.encode(tlv::serialize_tlv8(&msg)), + self.send_pairing_data(plist! ({ + "data": R::serialize_bytes(&tlv::serialize_tlv8(&msg)), "kind": "verifyManualPairing", "startNewSession": false - }}) + })) .await?; let res = self.receive_pairing_data().await?; - let res = match res.as_str() { - Some(r) => r, - None => { - warn!("Pairing data response was not a string"); - return Err(IdeviceError::UnexpectedResponse); - } - }; - debug!("Verify response: {res:#}"); - let data = B64.decode(res)?; + let data = match R::deserialize_bytes(res) { + Some(d) => d, + None => return Err(IdeviceError::UnexpectedResponse), + }; let data = tlv::deserialize_tlv8(&data)?; debug!("Verify TLV: {data:#?}"); @@ -231,34 +229,56 @@ impl<'a, R: ReadWrite> RemotePairingClient<'a, R> { } pub async fn send_pair_verified_failed(&mut self) -> Result<(), IdeviceError> { - self.send_plain_request(json! {{"event": {"_0": {"pairVerifyFailed": {}}}}}) - .await - } - - pub async fn attempt_pair_verify(&mut self) -> Result { - debug!("Sending attemptPairVerify"); - self.send_plain_request(json! { - { - "request": { - "_0": { - "handshake": { + self.inner + .send_plain( + plist!({ + "event": { "_0": { - "hostOptions": {"attemptPairVerify": true}, - "wireProtocolVersion": WIRE_PROTOCOL_VERSION, + "pairVerifyFailed": {} } } - } - } - } - }) - .await?; + }), + self.sequence_number, + ) + .await?; + self.sequence_number += 1; + Ok(()) + } + + pub async fn attempt_pair_verify(&mut self) -> Result { + debug!("Sending attemptPairVerify"); + self.inner + .send_plain( + plist!({ + "request": { + "_0": { + "handshake": { + "_0": { + "hostOptions": { + "attemptPairVerify": true + }, + "wireProtocolVersion": plist::Value::Integer(WIRE_PROTOCOL_VERSION.into()), + } + } + } + } + }), + self.sequence_number, + ) + .await?; + self.sequence_number += 1; + debug!("Waiting for attemptPairVerify response"); - let response = self.receive_plain_request().await?; + let response = self.inner.recv_plain().await?; let response = response - .get("response") + .as_dictionary() + .and_then(|x| x.get("response")) + .and_then(|x| x.as_dictionary()) .and_then(|x| x.get("_1")) + .and_then(|x| x.as_dictionary()) .and_then(|x| x.get("handshake")) + .and_then(|x| x.as_dictionary()) .and_then(|x| x.get("_0")); match response { @@ -301,38 +321,47 @@ impl<'a, R: ReadWrite> RemotePairingClient<'a, R> { data: vec![0x01], }, ]); - let tlv = B64.encode(tlv); - self.send_pairing_data(json! {{ + let tlv = R::serialize_bytes(&tlv); + self.send_pairing_data(plist!({ "data": tlv, "kind": "setupManualPairing", - "sendingHost": self.sending_host, + "sendingHost": &self.sending_host, "startNewSession": true - }}) + })) .await?; - let response = self.receive_plain_request().await?; - let response = &response["event"]["_0"]; + let response = self.inner.recv_plain().await?; + let response = match response + .get_by("event") + .and_then(|x| x.get_by("_0")) + .and_then(|x| x.as_dictionary()) + { + Some(r) => r, + None => { + return Err(IdeviceError::UnexpectedResponse); + } + }; + let mut pin = None; let pairing_data = match if let Some(err) = response.get("pairingRejectedWithError") { let context = err - .get("wrappedError") - .and_then(|x| x.get("userInfo")) - .and_then(|x| x.get("NSLocalizedDescription")) - .and_then(|x| x.as_str()) + .get_by("wrappedError") + .and_then(|x| x.get_by("userInfo")) + .and_then(|x| x.get_by("NSLocalizedDescription")) + .and_then(|x| x.as_string()) .map(|x| x.to_string()); return Err(IdeviceError::PairingRejected(context.unwrap_or_default())); } else if response.get("awaitingUserConsent").is_some() { pin = Some("000000".to_string()); - self.receive_pairing_data() - .await? - .as_str() - .map(|x| x.to_string()) + Some(self.receive_pairing_data().await?) } else { // On Apple TV, we can get the pin now - response["pairingData"]["_0"]["data"] - .as_str() - .map(|x| x.to_string()) + response + .get_by("pairingData") + .and_then(|x| x.get_by("_0")) + .and_then(|x| x.get_by("data")) + .map(|x| x.to_owned()) } { Some(p) => p, None => { @@ -340,7 +369,10 @@ impl<'a, R: ReadWrite> RemotePairingClient<'a, R> { } }; - let tlv = tlv::deserialize_tlv8(&B64.decode(pairing_data)?)?; + let tlv = tlv::deserialize_tlv8(&match R::deserialize_bytes(pairing_data) { + Some(t) => t, + None => return Err(IdeviceError::UnexpectedResponse), + })?; debug!("Received pairingData response: {tlv:#?}"); let mut salt = Vec::new(); @@ -427,25 +459,22 @@ impl<'a, R: ReadWrite> RemotePairingClient<'a, R> { data: client_proof.to_vec(), }, ]); - let tlv = B64.encode(tlv); + let tlv = R::serialize_bytes(&tlv); - self.send_pairing_data(json! {{ + self.send_pairing_data(plist!({ "data": tlv, "kind": "setupManualPairing", - "sendingHost": self.sending_host, + "sendingHost": &self.sending_host, "startNewSession": false, - }}) + })) .await?; let response = self.receive_pairing_data().await?; - let response = match response.as_str() { - Some(r) => tlv::deserialize_tlv8(&B64.decode(r)?)?, - None => { - warn!("Pairing data proof response was not a string"); - return Err(IdeviceError::UnexpectedResponse); - } - }; + let response = tlv::deserialize_tlv8(&match R::deserialize_bytes(response.to_owned()) { + Some(r) => r, + None => return Err(IdeviceError::UnexpectedResponse), + })?; debug!("Proof response: {response:#?}"); @@ -576,22 +605,22 @@ impl<'a, R: ReadWrite> RemotePairingClient<'a, R> { data: vec![0x05], }, ]); - let tlv = B64.encode(&tlv); + let tlv = R::serialize_bytes(&tlv); debug!("Sending encrypted data"); - self.send_pairing_data(json! {{ + self.send_pairing_data(plist!({ "data": tlv, "kind": "setupManualPairing", - "sendingHost": self.sending_host, + "sendingHost": &self.sending_host, "startNewSession": false, - }}) + })) .await?; debug!("Waiting for encrypted data"); - let response = match self.receive_pairing_data().await?.as_str() { - Some(r) => B64.decode(r)?, + let response = match R::deserialize_bytes(self.receive_pairing_data().await?) { + Some(r) => r, None => { - warn!("Pairing data response was not base64"); + warn!("Pairing data response was not deserializable"); return Err(IdeviceError::UnexpectedResponse); } }; @@ -630,89 +659,56 @@ impl<'a, R: ReadWrite> RemotePairingClient<'a, R> { async fn send_pairing_data( &mut self, - pairing_data: impl Serialize, + pairing_data: impl Serialize + PlistConvertible, ) -> Result<(), IdeviceError> { - self.send_plain_request(json! {{"event": {"_0": {"pairingData": {"_0": pairing_data}}}}}) - .await + self.inner + .send_plain( + plist!({ + "event": { + "_0": { + "pairingData": { + "_0": pairing_data + } + } + } + }), + self.sequence_number, + ) + .await?; + + self.sequence_number += 1; + Ok(()) } - async fn receive_pairing_data(&mut self) -> Result { - let response = self.receive_plain_request().await?; + async fn receive_pairing_data(&mut self) -> Result { + let response = self.inner.recv_plain().await?; - let response = match response.get("event").and_then(|x| x.get("_0")) { + let response = match response.get_by("event").and_then(|x| x.get_by("_0")) { Some(r) => r, None => return Err(IdeviceError::UnexpectedResponse), }; if let Some(data) = response - .get("pairingData") - .and_then(|x| x.get("_0")) - .and_then(|x| x.get("data")) + .get_by("pairingData") + .and_then(|x| x.get_by("_0")) + .and_then(|x| x.get_by("data")) { Ok(data.to_owned()) - } else if let Some(err) = response.get("pairingRejectedWithError") { + } else if let Some(err) = response.get_by("pairingRejectedWithError") { let context = err - .get("wrappedError") - .and_then(|x| x.get("userInfo")) - .and_then(|x| x.get("NSLocalizedDescription")) - .and_then(|x| x.as_str()) + .get_by("wrappedError") + .and_then(|x| x.get_by("userInfo")) + .and_then(|x| x.get_by("NSLocalizedDescription")) + .and_then(|x| x.as_string()) .map(|x| x.to_string()); Err(IdeviceError::PairingRejected(context.unwrap_or_default())) } else { Err(IdeviceError::UnexpectedResponse) } } - - async fn send_plain_request(&mut self, value: impl Serialize) -> Result<(), IdeviceError> { - self.send_rppairing(json!({ - "message": {"plain": {"_0": value}}, - "originatedBy": "host", - "sequenceNumber": self.sequence_number - })) - .await?; - - self.sequence_number += 1; - Ok(()) - } - - async fn receive_plain_request(&mut self) -> Result { - self.inner - .read_exact(&mut vec![0u8; RPPAIRING_MAGIC.len()]) - .await?; - - let mut packet_len_bytes = [0u8; 2]; - self.inner.read_exact(&mut packet_len_bytes).await?; - let packet_len = u16::from_be_bytes(packet_len_bytes); - - let mut value = vec![0u8; packet_len as usize]; - self.inner.read_exact(&mut value).await?; - - let value: serde_json::Value = serde_json::from_slice(&value)?; - let value = value - .get("message") - .and_then(|x| x.get("plain")) - .and_then(|x| x.get("_0")); - - match value { - Some(v) => Ok(v.to_owned()), - None => Err(IdeviceError::UnexpectedResponse), - } - } - - async fn send_rppairing(&mut self, value: impl Serialize) -> Result<(), IdeviceError> { - let value = serde_json::to_string(&value)?; - let x = value.as_bytes(); - - self.inner.write_all(RPPAIRING_MAGIC).await?; - self.inner - .write_all(&(x.len() as u16).to_be_bytes()) - .await?; - self.inner.write_all(x).await?; - Ok(()) - } } -impl std::fmt::Debug for RemotePairingClient<'_, R> { +impl std::fmt::Debug for RemotePairingClient<'_, R> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("RemotePairingClient") .field("inner", &self.inner) diff --git a/idevice/src/remote_pairing/socket.rs b/idevice/src/remote_pairing/socket.rs new file mode 100644 index 0000000..d114e60 --- /dev/null +++ b/idevice/src/remote_pairing/socket.rs @@ -0,0 +1,173 @@ +// Jackson Coxson + +use base64::{Engine as _, engine::general_purpose::STANDARD as B64}; +use plist_macro::{plist, pretty_print_plist}; +use serde::Serialize; +use serde_json::json; +use std::{fmt::Debug, pin::Pin}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tracing::{debug, warn}; + +use crate::{ + IdeviceError, ReadWrite, RemoteXpcClient, remote_pairing::RPPAIRING_MAGIC, xpc::XPCObject, +}; + +pub trait RpPairingSocketProvider: Debug { + fn send_plain( + &mut self, + value: impl Serialize, + seq: usize, + ) -> Pin> + Send + '_>>; + + fn recv_plain<'a>( + &'a mut self, + ) -> Pin> + Send + 'a>>; + + /// rppairing uses b64, while RemoteXPC uses raw bytes just fine + fn serialize_bytes(b: &[u8]) -> plist::Value; + fn deserialize_bytes(v: plist::Value) -> Option>; +} + +#[derive(Debug)] +pub struct RpPairingSocket { + pub inner: R, +} + +impl RpPairingSocket { + pub fn new(socket: R) -> Self { + Self { inner: socket } + } + + async fn send_rppairing(&mut self, value: impl Serialize) -> Result<(), IdeviceError> { + let value = serde_json::to_string(&value)?; + let x = value.as_bytes(); + + self.inner.write_all(RPPAIRING_MAGIC).await?; + self.inner + .write_all(&(x.len() as u16).to_be_bytes()) + .await?; + self.inner.write_all(x).await?; + Ok(()) + } +} + +impl RpPairingSocketProvider for RpPairingSocket { + fn send_plain( + &mut self, + value: impl Serialize, + seq: usize, + ) -> Pin> + Send + '_>> { + let v = json!({ + "message": {"plain": {"_0": value}}, + "originatedBy": "host", + "sequenceNumber": seq + }); + + Box::pin(async move { + self.send_rppairing(v).await?; + Ok(()) + }) + } + + fn recv_plain<'a>( + &'a mut self, + ) -> Pin> + Send + 'a>> { + Box::pin(async move { + self.inner + .read_exact(&mut vec![0u8; RPPAIRING_MAGIC.len()]) + .await?; + + let mut packet_len_bytes = [0u8; 2]; + self.inner.read_exact(&mut packet_len_bytes).await?; + let packet_len = u16::from_be_bytes(packet_len_bytes); + + let mut value = vec![0u8; packet_len as usize]; + self.inner.read_exact(&mut value).await?; + + let value: serde_json::Value = serde_json::from_slice(&value)?; + let value = value + .get("message") + .and_then(|x| x.get("plain")) + .and_then(|x| x.get("_0")); + + match value { + Some(v) => Ok(plist::to_value(v).unwrap()), + None => Err(IdeviceError::UnexpectedResponse), + } + }) + } + + fn serialize_bytes(b: &[u8]) -> plist::Value { + plist!(B64.encode(b)) + } + + fn deserialize_bytes(v: plist::Value) -> Option> { + if let plist::Value::String(v) = v { + B64.decode(v).ok() + } else { + None + } + } +} + +impl RpPairingSocketProvider for RemoteXpcClient { + fn send_plain( + &mut self, + value: impl Serialize, + seq: usize, + ) -> Pin> + Send + '_>> { + let value: plist::Value = plist::to_value(&value).expect("plist assert failed"); + let value: XPCObject = value.into(); + + let v = crate::xpc!({ + "mangledTypeName": "RemotePairing.ControlChannelMessageEnvelope", + "value": { + "message": {"plain": {"_0": value}}, + "originatedBy": "host", + "sequenceNumber": seq as u64 + } + }); + debug!("Sending XPC: {v:#?}"); + + Box::pin(async move { + self.send_object(v, true).await?; + Ok(()) + }) + } + + fn recv_plain<'a>( + &'a mut self, + ) -> Pin> + Send + 'a>> { + Box::pin(async move { + let msg = self.recv_root().await.unwrap(); + debug!("Received RemoteXPC {}", pretty_print_plist(&msg)); + let value = msg + .into_dictionary() + .and_then(|mut x| x.remove("value")) + .and_then(|x| x.into_dictionary()) + .and_then(|mut x| x.remove("message")) + .and_then(|x| x.into_dictionary()) + .and_then(|mut x| x.remove("plain")) + .and_then(|x| x.into_dictionary()) + .and_then(|mut x| x.remove("_0")); + + match value { + Some(v) => Ok(v), + None => Err(IdeviceError::UnexpectedResponse), + } + }) + } + + fn serialize_bytes(b: &[u8]) -> plist::Value { + plist::Value::Data(b.to_owned()) + } + + fn deserialize_bytes(v: plist::Value) -> Option> { + if let plist::Value::Data(v) = v { + Some(v) + } else { + warn!("Non-data passed to rppairingsocket::deserialize_bytes for RemoteXPC provider"); + None + } + } +} diff --git a/idevice/src/xpc/mod.rs b/idevice/src/xpc/mod.rs index 7b897bc..4afe52d 100644 --- a/idevice/src/xpc/mod.rs +++ b/idevice/src/xpc/mod.rs @@ -28,7 +28,7 @@ impl RemoteXpcClient { pub async fn new(socket: R) -> Result { Ok(Self { h2_client: http2::Http2Client::new(socket).await?, - root_id: 0, + root_id: 1, }) } diff --git a/tools/src/pair_apple_tv.rs b/tools/src/pair_apple_tv.rs index 5dfb683..915222c 100644 --- a/tools/src/pair_apple_tv.rs +++ b/tools/src/pair_apple_tv.rs @@ -5,7 +5,7 @@ use std::{io::Write, net::IpAddr, str::FromStr}; use clap::{Arg, Command}; -use idevice::remote_pairing::{RemotePairingClient, RpPairingFile}; +use idevice::remote_pairing::{RemotePairingClient, RpPairingFile, RpPairingSocket}; #[tokio::main] async fn main() { @@ -49,6 +49,7 @@ async fn main() { tokio::net::TcpStream::connect((IpAddr::from_str(ip).expect("failed to parse IP"), port)) .await .expect("Failed to connect"); + let conn = RpPairingSocket::new(conn); let host = "idevice-rs-jkcoxson"; let mut rpf = RpPairingFile::generate(host); @@ -74,6 +75,8 @@ async fn main() { tokio::net::TcpStream::connect((IpAddr::from_str(ip).expect("failed to parse IP"), port)) .await .expect("Failed to connect"); + let conn = RpPairingSocket::new(conn); + let mut rpc = RemotePairingClient::new(conn, host, &mut rpf); rpc.connect( async |_| { diff --git a/tools/src/pair_rsd_ios.rs b/tools/src/pair_rsd_ios.rs index 84998ca..4f24310 100644 --- a/tools/src/pair_rsd_ios.rs +++ b/tools/src/pair_rsd_ios.rs @@ -7,7 +7,12 @@ use std::{any::Any, sync::Arc, time::Duration}; use clap::{Arg, Command}; -use idevice::{RemoteXpcClient, rsd::RsdHandshake, xpc}; +use idevice::{ + RemoteXpcClient, + remote_pairing::{RemotePairingClient, RpPairingFile}, + rsd::RsdHandshake, +}; +use tokio::net::TcpStream; use zeroconf::{ BrowserEvent, MdnsBrowser, ServiceType, prelude::{TEventLoop, TMdnsBrowser}, @@ -57,28 +62,7 @@ fn on_service_discovered( tokio::task::spawn(async move { println!("Found iOS device to pair with!! - {result:?}"); - let looked_up = tokio::net::lookup_host(format!("{}:{}", result.host_name(), 58783)) - .await - .unwrap(); - - let mut stream = None; - for l in looked_up { - if l.is_ipv4() { - continue; - } - - println!("Found IP: {l:?}"); - - match tokio::net::TcpStream::connect(l).await { - Ok(s) => { - println!("connected with local addr {:?}", s.local_addr()); - stream = Some(s); - break; - } - Err(e) => println!("failed to connect: {e:?}"), - } - } - let stream = match stream { + let stream = match lookup_host_and_connect(result.host_name(), 58783).await { Some(s) => s, None => { println!("Couldn't open TCP port on device"); @@ -88,7 +72,65 @@ fn on_service_discovered( let handshake = RsdHandshake::new(stream).await.expect("no rsd"); - println!("handshake: {handshake:?}"); + println!("handshake: {handshake:#?}"); + + let ts = handshake + .services + .get("com.apple.internal.dt.coredevice.untrusted.tunnelservice") + .unwrap(); + + println!("connecting to tunnel service"); + let stream = lookup_host_and_connect(result.host_name(), ts.port) + .await + .expect("failed to connect to tunnselservice"); + let mut conn = RemoteXpcClient::new(stream).await.unwrap(); + + println!("doing tunnel service handshake"); + conn.do_handshake().await.unwrap(); + + let msg = conn.recv_root().await.unwrap(); + println!("{msg:#?}"); + + let host = "idevice-rs-jkcoxson"; + let mut rpf = RpPairingFile::generate(host); + let mut rpc = RemotePairingClient::new(conn, host, &mut rpf); + rpc.connect( + async |_| "000000".to_string(), + 0u8, // we need no state, so pass a single byte that will hopefully get optimized out + ) + .await + .expect("no pair"); + + rpf.write_to_file("ios_pairing_file.plist").await.unwrap(); + println!( + "congrats you're paired now, the rppairing record has been saved. Have a nice day." + ); }); } } + +async fn lookup_host_and_connect(host: &str, port: u16) -> Option { + let looked_up = tokio::net::lookup_host(format!("{}:{}", host, port)) + .await + .unwrap(); + + let mut stream = None; + for l in looked_up { + if l.is_ipv4() { + continue; + } + + println!("Found IP: {l:?}"); + + match tokio::net::TcpStream::connect(l).await { + Ok(s) => { + println!("connected with local addr {:?}", s.local_addr()); + stream = Some(s); + break; + } + Err(e) => println!("failed to connect: {e:?}"), + } + } + + stream +}